From 4c5c552c343592ca946a25a9807e442ced9c83e6 Mon Sep 17 00:00:00 2001 From: Planet-Lab Support Date: Fri, 10 Sep 2004 19:18:38 +0000 Subject: [PATCH] This commit was manufactured by cvs2svn to create branch 'lkcd'. --- Documentation/COPYING.modules | 708 -- Documentation/arm/IXP4xx | 155 + Documentation/arm/SA1100/PCMCIA | 374 - Documentation/arm/XScale/ADIFCC/80200EVB | 110 - Documentation/arm/XScale/IOP3XX/IQ80310 | 247 - Documentation/arm/XScale/IOP3XX/IQ80321 | 215 - Documentation/arm/XScale/IOP3XX/aau.txt | 178 - Documentation/arm/XScale/IOP3XX/dma.txt | 214 - Documentation/arm/XScale/IOP3XX/message.txt | 110 - Documentation/arm/XScale/IOP3XX/pmon.txt | 71 - Documentation/arm/XScale/cache-lock.txt | 123 - Documentation/arm/XScale/pmu.txt | 168 - Documentation/arm/XScale/tlb-lock.txt | 64 - Documentation/cpu-freq/amd-powernow.txt | 38 + Documentation/filesystems/relayfs.txt | 812 --- Documentation/sound/alsa/Audigy-mixer.txt | 345 + arch/arm/configs/ixp4xx_defconfig | 1081 +++ arch/arm/configs/mainstone_defconfig | 743 ++ arch/arm/configs/smdk2410_defconfig | 667 ++ arch/arm/mach-ixp4xx/Makefile | 10 + arch/arm/mach-ixp4xx/common-pci.c | 543 ++ arch/arm/mach-ixp4xx/common.c | 263 + arch/arm/mach-ixp4xx/coyote-pci.c | 69 + arch/arm/mach-ixp4xx/ixdp425-pci.c | 84 + arch/arm/mach-ixp4xx/prpmc1100-pci.c | 119 + arch/arm/mach-ixp4xx/prpmc1100-setup.c | 90 + arch/arm/mach-omap/innovator1510.c | 99 - arch/arm/mach-omap/innovator1610.c | 91 - arch/arm/mach-omap/irq.h | 172 - arch/arm/mach-omap/omap-generic.c | 77 - arch/arm/mach-omap/omap-perseus2.c | 116 - arch/arm/mach-s3c2410/mach-smdk2410.c | 109 + arch/cris/arch-v10/drivers/ide.c | 945 +++ arch/cris/kernel/{ksyms.c => crisksyms.c} | 10 +- arch/cris/kernel/hexify.c | 31 - arch/i386/kernel/entry_trampoline.c | 75 - arch/i386/mach-es7000/es7000.c | 279 - arch/i386/mach-es7000/setup.c | 106 - arch/i386/mach-es7000/topology.c | 64 - arch/i386/mach-generic/es7000.c | 28 + arch/ia64/configs/sim_defconfig | 535 ++ arch/ia64/dig/topology.c | 43 + arch/ia64/kernel/perfmon_hpsim.h | 75 - arch/ia64/lib/bitop.c | 88 + arch/mips/au1000/common/cputable.c | 56 + arch/parisc/kernel/unwind.c | 295 + arch/ppc/kernel/dma-mapping.c | 439 ++ arch/ppc/kernel/vecemu.c | 346 + arch/ppc/kernel/vector.S | 217 + arch/ppc/mm/cachemap.c | 174 - arch/ppc/ocp/Makefile | 6 - arch/ppc/ocp/ocp-driver.c | 195 - arch/ppc/ocp/ocp-probe.c | 113 - arch/ppc/ocp/ocp.c | 109 - arch/ppc/platforms/4xx/bubinga.c | 263 + arch/ppc/platforms/4xx/bubinga.h | 69 + arch/ppc/platforms/4xx/ibm405ep.c | 134 + arch/ppc/platforms/4xx/ibm405ep.h | 148 + arch/ppc/platforms/sbc82xx.c | 113 + arch/ppc/platforms/sbc82xx.h | 24 + arch/ppc/syslib/dcr.S | 41 + arch/ppc/syslib/ibm440gx_common.c | 212 + arch/ppc/syslib/ibm440gx_common.h | 54 + arch/ppc/syslib/ibm44x_common.h | 36 + arch/ppc/syslib/ocp.c | 485 ++ arch/ppc64/lib/locks.c | 285 + arch/s390/lib/memset.S | 30 - arch/s390/lib/memset64.S | 30 - arch/s390/lib/strcmp.S | 27 - arch/s390/lib/strcmp64.S | 27 - arch/s390/lib/strcpy.S | 20 - arch/s390/lib/strcpy64.S | 20 - arch/s390/lib/string.c | 405 ++ arch/s390/lib/strncpy.S | 35 - arch/s390/lib/strncpy64.S | 35 - arch/sparc64/lib/find_bit.c | 125 + arch/sparc64/lib/splock.S | 23 + arch/um/drivers/cow.h | 41 - arch/um/drivers/cow_user.c | 375 - arch/um/include/irq_kern.h | 28 - arch/um/include/mem_kern.h | 30 - arch/um/kernel/physmem.c | 468 -- arch/um/kernel/skas/uaccess.c | 219 - arch/um/kernel/tt/uaccess.c | 73 - arch/um/os-Linux/user_syms.c | 88 - arch/x86_64/kernel/domain.c | 93 + drivers/char/drm/drm_irq.h | 371 + drivers/char/drm/drm_pciids.h | 203 + drivers/char/dz.c | 1540 ----- drivers/char/dz.h | 230 - drivers/char/sh-sci.c | 1646 ----- drivers/char/sh-sci.h | 478 -- drivers/char/watchdog/ixp4xx_wdt.c | 233 + drivers/dump/Makefile | 16 + drivers/dump/dump_arm.c | 250 + drivers/dump/dump_blockdev.c | 468 ++ drivers/dump/dump_execute.c | 142 + drivers/dump/dump_filters.c | 143 + drivers/dump/dump_fmt.c | 406 ++ drivers/dump/dump_gzip.c | 118 + drivers/dump/dump_i386.c | 344 + drivers/dump/dump_memdev.c | 640 ++ drivers/dump/dump_methods.h | 349 + drivers/dump/dump_netdev.c | 867 +++ drivers/dump/dump_overlay.c | 884 +++ drivers/dump/dump_ppc64.c | 436 ++ drivers/dump/dump_rle.c | 175 + drivers/dump/dump_scheme.c | 383 ++ drivers/dump/dump_setup.c | 835 +++ drivers/i2c/busses/i2c-ixp42x.c | 176 - drivers/i2c/busses/i2c-ixp4xx.c | 181 + drivers/i2c/chips/max1619.c | 378 ++ drivers/i2c/chips/rtc8564.c | 396 ++ drivers/i2c/chips/rtc8564.h | 78 + drivers/ide/h8300/ide-h8300.c | 119 + drivers/ide/ide-tcq.c | 808 --- drivers/ide/pci/alim15x3.h | 37 - drivers/ide/pci/amd74xx.h | 168 - drivers/ide/pci/cmd640.h | 32 - drivers/ide/pci/cs5520.h | 48 - drivers/ide/pci/cs5530.h | 34 - drivers/ide/pci/ns87415.h | 31 - drivers/ide/pci/rz1000.h | 45 - drivers/ide/pci/sc1200.h | 34 - drivers/ide/pci/siimage.h | 73 - drivers/ide/pci/sis5513.h | 34 - drivers/ide/pci/sl82c105.h | 34 - drivers/ide/pci/slc90e66.h | 36 - drivers/ide/pci/triflex.h | 42 - drivers/ide/pci/trm290.h | 32 - drivers/ide/pci/via82cxxx.h | 44 - drivers/ide/ppc/swarm.c | 101 - drivers/mtd/maps/ixp4xx.c | 244 + drivers/mtd/maps/wr_sbc82xx_flash.c | 167 + drivers/net/auto_irq.c | 68 - drivers/net/ibm_emac/ibm_emac.h | 263 + drivers/net/ibm_emac/ibm_emac_core.h | 146 + drivers/net/ibm_emac/ibm_emac_debug.c | 224 + drivers/net/ibm_emac/ibm_emac_mal.c | 467 ++ drivers/net/ibm_emac/ibm_emac_mal.h | 130 + drivers/net/ibm_emac/ibm_emac_phy.c | 297 + drivers/net/ibm_emac/ibm_emac_rgmii.h | 65 + drivers/net/ibm_emac/ibm_emac_tah.h | 48 + drivers/net/ibm_emac/ibm_emac_zmii.h | 93 + drivers/net/ne-h8300.c | 666 ++ drivers/net/rcif.h | 292 - drivers/net/rclanmtl.c | 2029 ------ drivers/net/rclanmtl.h | 701 -- drivers/net/rcpci45.c | 1049 --- drivers/net/wan/comx-hw-comx.c | 1450 ---- drivers/net/wan/comx-hw-locomx.c | 496 -- drivers/net/wan/comx-hw-mixcom.c | 960 --- drivers/net/wan/comx-hw-munich.c | 2854 -------- drivers/net/wan/comx-proto-fr.c | 1014 --- drivers/net/wan/comx-proto-lapb.c | 551 -- drivers/net/wan/comx-proto-ppp.c | 269 - drivers/net/wan/comx.c | 1128 --- drivers/net/wan/comx.h | 232 - drivers/net/wan/comxhw.h | 113 - drivers/net/wan/falc-lh.h | 102 - drivers/net/wan/hscx.h | 103 - drivers/net/wan/mixcom.h | 35 - drivers/net/wan/munich32x.h | 191 - drivers/net/wan/wanxlfw.inc | 158 - drivers/pcmcia/pxa2xx_base.h | 3 + drivers/pcmcia/sa1100.h | 164 - drivers/pcmcia/sa11xx_core.c | 971 --- drivers/pcmcia/sa11xx_core.h | 121 - drivers/scsi/ipr.c | 6021 +++++++++++++++++ drivers/scsi/ipr.h | 1252 ++++ drivers/scsi/pcmcia/qlogic_core.c | 2 - drivers/scsi/pcmcia/sym53c500_cs.c | 1042 +++ drivers/scsi/{qlogicfas.h => qlogicfas408.h} | 58 +- drivers/scsi/sata_promise.h | 154 + drivers/usb/core/{driverfs.c => sysfs.c} | 8 +- drivers/usb/input/touchkitusb.c | 310 + drivers/usb/misc/phidgetservo.c | 327 + drivers/video/asiliantfb.c | 620 ++ drivers/video/gbefb.c | 1200 ++++ drivers/video/pxafb.h | 129 + fs/ext3/resize.c | 956 --- fs/hostfs/Makefile | 26 - fs/hostfs/hostfs.h | 79 - fs/hostfs/hostfs_kern.c | 1008 --- fs/hostfs/hostfs_user.c | 361 - fs/hppfs/Makefile | 19 - fs/hppfs/hppfs_kern.c | 811 --- fs/intermezzo/Makefile | 11 - fs/intermezzo/cache.c | 207 - fs/intermezzo/dcache.c | 342 - fs/intermezzo/dir.c | 1333 ---- fs/intermezzo/ext_attr.c | 197 - fs/intermezzo/file.c | 534 -- fs/intermezzo/fileset.c | 674 -- fs/intermezzo/inode.c | 179 - fs/intermezzo/intermezzo_fs.h | 923 --- fs/intermezzo/intermezzo_idl.h | 304 - fs/intermezzo/intermezzo_journal.h | 24 - fs/intermezzo/intermezzo_kml.h | 260 - fs/intermezzo/intermezzo_lib.h | 162 - fs/intermezzo/intermezzo_psdev.h | 55 - fs/intermezzo/intermezzo_upcall.h | 146 - fs/intermezzo/journal.c | 2452 ------- fs/intermezzo/journal_ext2.c | 90 - fs/intermezzo/journal_ext3.c | 283 - fs/intermezzo/journal_obdfs.c | 193 - fs/intermezzo/journal_reiserfs.c | 140 - fs/intermezzo/journal_tmpfs.c | 107 - fs/intermezzo/journal_xfs.c | 161 - fs/intermezzo/kml.c | 194 - fs/intermezzo/kml_decode.c | 1016 --- fs/intermezzo/kml_reint.c | 647 -- fs/intermezzo/kml_setup.c | 58 - fs/intermezzo/kml_unpack.c | 712 -- fs/intermezzo/kml_utils.c | 43 - fs/intermezzo/methods.c | 493 -- fs/intermezzo/presto.c | 736 -- fs/intermezzo/psdev.c | 647 -- fs/intermezzo/replicator.c | 290 - fs/intermezzo/super.c | 407 -- fs/intermezzo/sysctl.c | 368 - fs/intermezzo/upcall.c | 559 -- fs/intermezzo/vfs.c | 2416 ------- fs/rcfs/Makefile | 10 - fs/rcfs/dir.c | 336 - fs/rcfs/inode.c | 204 - fs/rcfs/magic.c | 546 -- fs/rcfs/rootdir.c | 244 - fs/rcfs/socket_fs.c | 338 - fs/rcfs/super.c | 288 - fs/rcfs/tc_magic.c | 94 - fs/reiserfs/xattr.c | 1441 ++++ fs/reiserfs/xattr_acl.c | 563 ++ fs/relayfs/Makefile | 8 - fs/relayfs/inode.c | 629 -- fs/relayfs/klog.c | 206 - fs/relayfs/relay.c | 1911 ------ fs/relayfs/relay_locking.c | 322 - fs/relayfs/relay_locking.h | 34 - fs/relayfs/relay_lockless.c | 541 -- fs/relayfs/relay_lockless.h | 34 - fs/relayfs/resize.c | 1091 --- fs/relayfs/resize.h | 51 - fs/xfs/{linux => linux-2.6}/kmem.h | 24 +- fs/xfs/{linux => linux-2.6}/mrlock.h | 0 fs/xfs/{linux => linux-2.6}/sema.h | 0 fs/xfs/{linux => linux-2.6}/sv.h | 0 fs/xfs/{linux => linux-2.6}/xfs_aops.c | 44 +- fs/xfs/{linux => linux-2.6}/xfs_buf.c | 27 +- fs/xfs/{linux => linux-2.6}/xfs_buf.h | 0 fs/xfs/{linux => linux-2.6}/xfs_file.c | 6 +- fs/xfs/{linux => linux-2.6}/xfs_fs_subr.c | 0 fs/xfs/{linux => linux-2.6}/xfs_globals.c | 10 +- fs/xfs/{linux => linux-2.6}/xfs_ioctl.c | 38 +- fs/xfs/{linux => linux-2.6}/xfs_iops.h | 0 fs/xfs/{linux => linux-2.6}/xfs_linux.h | 10 +- fs/xfs/{linux => linux-2.6}/xfs_lrw.c | 2 +- fs/xfs/{linux => linux-2.6}/xfs_lrw.h | 0 fs/xfs/{linux => linux-2.6}/xfs_stats.c | 2 +- fs/xfs/{linux => linux-2.6}/xfs_super.c | 25 +- fs/xfs/{linux => linux-2.6}/xfs_super.h | 2 +- fs/xfs/{linux => linux-2.6}/xfs_sysctl.c | 14 +- fs/xfs/{linux => linux-2.6}/xfs_sysctl.h | 27 +- fs/xfs/{linux => linux-2.6}/xfs_vfs.c | 1 + fs/xfs/{linux => linux-2.6}/xfs_vfs.h | 4 +- fs/xfs/{linux => linux-2.6}/xfs_vnode.c | 0 fs/xfs/{linux => linux-2.6}/xfs_vnode.h | 0 fs/xfs/linux/mutex.h | 53 - fs/xfs/linux/spin.h | 74 - fs/xfs/linux/time.h | 51 - fs/xfs/linux/xfs_cred.h | 50 - fs/xfs/linux/xfs_fs_subr.h | 49 - fs/xfs/linux/xfs_globals.h | 44 - fs/xfs/linux/xfs_iops.c | 708 -- fs/xfs/linux/xfs_stats.h | 164 - fs/xfs/linux/xfs_version.h | 44 - include/asm-alpha/relay.h | 5 - include/asm-alpha/rmap.h | 7 - include/asm-arm/arch-cl7500/ide.h | 50 - include/asm-arm/arch-cl7500/keyboard.h | 16 - include/asm-arm/arch-clps711x/keyboard.h | 26 - include/asm-arm/arch-ebsa110/ide.h | 1 - include/asm-arm/arch-ebsa285/ide.h | 49 - include/asm-arm/arch-iop3xx/ide.h | 49 - include/asm-arm/arch-ixp4xx/dma.h | 52 + include/asm-arm/arch-ixp4xx/io.h | 388 ++ include/asm-arm/arch-ixp4xx/irq.h | 13 + include/asm-arm/arch-ixp4xx/memory.h | 27 + include/asm-arm/arch-ixp4xx/param.h | 3 + include/asm-arm/arch-ixp4xx/platform.h | 116 + include/asm-arm/arch-ixp4xx/serial.h | 27 + include/asm-arm/arch-ixp4xx/system.h | 43 + include/asm-arm/arch-ixp4xx/time.h | 7 + include/asm-arm/arch-ixp4xx/uncompress.h | 64 + include/asm-arm/arch-l7200/ide.h | 27 - include/asm-arm/arch-l7200/keyboard.h | 51 - include/asm-arm/arch-nexuspci/ide.h | 37 - include/asm-arm/arch-omap/omap-h2.h | 35 - include/asm-arm/arch-omap/omap-innovator.h | 214 - include/asm-arm/arch-omap/omap-perseus2.h | 152 - include/asm-arm/arch-pxa/ide.h | 54 - include/asm-arm/arch-pxa/keyboard.h | 28 - include/asm-arm/arch-pxa/pxafb.h | 68 + include/asm-arm/arch-rpc/ide.h | 48 - include/asm-arm/arch-s3c2410/ide.h | 49 - include/asm-arm/arch-sa1100/keyboard.h | 23 - include/asm-arm/arch-shark/ide.h | 47 - include/asm-arm/arch-shark/keyboard.h | 68 - include/asm-arm/arch-tbox/ide.h | 3 - include/asm-arm/relay.h | 5 - include/asm-arm/rmap.h | 6 - include/asm-arm26/relay.h | 5 - include/asm-arm26/rmap.h | 66 - include/asm-cris/relay.h | 5 - include/asm-cris/rmap.h | 7 - include/asm-generic/relay.h | 76 - include/asm-generic/rmap.h | 91 - include/asm-h8300/aki3068net/machine-depend.h | 29 - include/asm-h8300/edosk2674/machine-depend.h | 70 - include/asm-h8300/generic/machine-depend.h | 17 - include/asm-h8300/generic/timer_rate.h | 15 - include/asm-h8300/h8300_smsc.h | 20 - include/asm-h8300/h8max/machine-depend.h | 100 - include/asm-h8300/relay.h | 5 - include/asm-i386/dump.h | 93 + include/asm-i386/relay.h | 101 - include/asm-i386/rmap.h | 21 - include/asm-ia64/relay.h | 5 - include/asm-ia64/rmap.h | 7 - include/asm-m68k/relay.h | 5 - include/asm-m68k/rmap.h | 7 - include/asm-m68knommu/relay.h | 5 - include/asm-m68knommu/rmap.h | 2 - include/asm-mips/relay.h | 5 - include/asm-mips/rmap.h | 7 - include/asm-mips64/relay.h | 5 - include/asm-parisc/relay.h | 5 - include/asm-parisc/rmap.h | 7 - include/asm-ppc/relay.h | 5 - include/asm-ppc/rmap.h | 9 - include/asm-ppc64/relay.h | 5 - include/asm-ppc64/rmap.h | 9 - include/asm-s390/relay.h | 5 - include/asm-s390/rmap.h | 7 - include/asm-sh/relay.h | 5 - include/asm-sh/rmap.h | 7 - include/asm-sparc/relay.h | 5 - include/asm-sparc/rmap.h | 7 - include/asm-sparc64/relay.h | 5 - include/asm-sparc64/rmap.h | 7 - include/asm-um/cpufeature.h | 6 - include/asm-um/local.h | 6 - include/asm-um/module-generic.h | 6 - include/asm-um/rmap.h | 6 - include/asm-um/sections.h | 7 - include/asm-v850/relay.h | 5 - include/asm-v850/rmap.h | 1 - include/asm-x86_64/relay.h | 5 - include/asm-x86_64/rmap.h | 7 - include/linux/ckrm.h | 156 - include/linux/ckrm_ce.h | 91 - include/linux/ckrm_net.h | 41 - include/linux/ckrm_rc.h | 367 - include/linux/ckrm_tc.h | 18 - include/linux/ckrm_tsk.h | 41 - include/linux/dump.h | 385 ++ include/linux/dump_netdev.h | 80 + include/linux/dumpdev.h | 161 + include/linux/klog.h | 24 - include/linux/mempolicy.h | 221 + include/linux/rcfs.h | 98 - include/linux/reiserfs_acl.h | 91 + include/linux/reiserfs_xattr.h | 132 + include/linux/relayfs_fs.h | 686 -- include/linux/taskdelays.h | 20 - init/kerntypes.c | 31 + kernel/ckrm/Makefile | 14 - kernel/ckrm/ckrm.c | 1009 --- kernel/ckrm/ckrm_listenaq.c | 503 -- kernel/ckrm/ckrm_sockc.c | 554 -- kernel/ckrm/ckrm_tasks.c | 509 -- kernel/ckrm/ckrm_tc.c | 785 --- kernel/ckrm/ckrmutils.c | 207 - net/bluetooth/syms.c | 84 - net/bridge/br_sysfs_br.c | 383 ++ scripts/checkstack.pl | 108 + scripts/reference_init.pl | 102 + sound/pci/ice1712/prodigy.c | 663 -- sound/pci/ice1712/prodigy.h | 67 - 389 files changed, 36883 insertions(+), 64154 deletions(-) delete mode 100644 Documentation/COPYING.modules create mode 100644 Documentation/arm/IXP4xx delete mode 100644 Documentation/arm/SA1100/PCMCIA delete mode 100644 Documentation/arm/XScale/ADIFCC/80200EVB delete mode 100644 Documentation/arm/XScale/IOP3XX/IQ80310 delete mode 100644 Documentation/arm/XScale/IOP3XX/IQ80321 delete mode 100644 Documentation/arm/XScale/IOP3XX/aau.txt delete mode 100644 Documentation/arm/XScale/IOP3XX/dma.txt delete mode 100644 Documentation/arm/XScale/IOP3XX/message.txt delete mode 100644 Documentation/arm/XScale/IOP3XX/pmon.txt delete mode 100644 Documentation/arm/XScale/cache-lock.txt delete mode 100644 Documentation/arm/XScale/pmu.txt delete mode 100644 Documentation/arm/XScale/tlb-lock.txt create mode 100644 Documentation/cpu-freq/amd-powernow.txt delete mode 100644 Documentation/filesystems/relayfs.txt create mode 100644 Documentation/sound/alsa/Audigy-mixer.txt create mode 100644 arch/arm/configs/ixp4xx_defconfig create mode 100644 arch/arm/configs/mainstone_defconfig create mode 100644 arch/arm/configs/smdk2410_defconfig create mode 100644 arch/arm/mach-ixp4xx/Makefile create mode 100644 arch/arm/mach-ixp4xx/common-pci.c create mode 100644 arch/arm/mach-ixp4xx/common.c create mode 100644 arch/arm/mach-ixp4xx/coyote-pci.c create mode 100644 arch/arm/mach-ixp4xx/ixdp425-pci.c create mode 100644 arch/arm/mach-ixp4xx/prpmc1100-pci.c create mode 100644 arch/arm/mach-ixp4xx/prpmc1100-setup.c delete mode 100644 arch/arm/mach-omap/innovator1510.c delete mode 100644 arch/arm/mach-omap/innovator1610.c delete mode 100644 arch/arm/mach-omap/irq.h delete mode 100644 arch/arm/mach-omap/omap-generic.c delete mode 100644 arch/arm/mach-omap/omap-perseus2.c create mode 100644 arch/arm/mach-s3c2410/mach-smdk2410.c create mode 100644 arch/cris/arch-v10/drivers/ide.c rename arch/cris/kernel/{ksyms.c => crisksyms.c} (91%) delete mode 100644 arch/cris/kernel/hexify.c delete mode 100644 arch/i386/kernel/entry_trampoline.c delete mode 100644 arch/i386/mach-es7000/es7000.c delete mode 100644 arch/i386/mach-es7000/setup.c delete mode 100644 arch/i386/mach-es7000/topology.c create mode 100644 arch/i386/mach-generic/es7000.c create mode 100644 arch/ia64/configs/sim_defconfig create mode 100644 arch/ia64/dig/topology.c delete mode 100644 arch/ia64/kernel/perfmon_hpsim.h create mode 100644 arch/ia64/lib/bitop.c create mode 100644 arch/mips/au1000/common/cputable.c create mode 100644 arch/parisc/kernel/unwind.c create mode 100644 arch/ppc/kernel/dma-mapping.c create mode 100644 arch/ppc/kernel/vecemu.c create mode 100644 arch/ppc/kernel/vector.S delete mode 100644 arch/ppc/mm/cachemap.c delete mode 100644 arch/ppc/ocp/Makefile delete mode 100644 arch/ppc/ocp/ocp-driver.c delete mode 100644 arch/ppc/ocp/ocp-probe.c delete mode 100644 arch/ppc/ocp/ocp.c create mode 100644 arch/ppc/platforms/4xx/bubinga.c create mode 100644 arch/ppc/platforms/4xx/bubinga.h create mode 100644 arch/ppc/platforms/4xx/ibm405ep.c create mode 100644 arch/ppc/platforms/4xx/ibm405ep.h create mode 100644 arch/ppc/platforms/sbc82xx.c create mode 100644 arch/ppc/platforms/sbc82xx.h create mode 100644 arch/ppc/syslib/dcr.S create mode 100644 arch/ppc/syslib/ibm440gx_common.c create mode 100644 arch/ppc/syslib/ibm440gx_common.h create mode 100644 arch/ppc/syslib/ibm44x_common.h create mode 100644 arch/ppc/syslib/ocp.c create mode 100644 arch/ppc64/lib/locks.c delete mode 100644 arch/s390/lib/memset.S delete mode 100644 arch/s390/lib/memset64.S delete mode 100644 arch/s390/lib/strcmp.S delete mode 100644 arch/s390/lib/strcmp64.S delete mode 100644 arch/s390/lib/strcpy.S delete mode 100644 arch/s390/lib/strcpy64.S create mode 100644 arch/s390/lib/string.c delete mode 100644 arch/s390/lib/strncpy.S delete mode 100644 arch/s390/lib/strncpy64.S create mode 100644 arch/sparc64/lib/find_bit.c create mode 100644 arch/sparc64/lib/splock.S delete mode 100644 arch/um/drivers/cow.h delete mode 100644 arch/um/drivers/cow_user.c delete mode 100644 arch/um/include/irq_kern.h delete mode 100644 arch/um/include/mem_kern.h delete mode 100644 arch/um/kernel/physmem.c delete mode 100644 arch/um/kernel/skas/uaccess.c delete mode 100644 arch/um/kernel/tt/uaccess.c delete mode 100644 arch/um/os-Linux/user_syms.c create mode 100644 arch/x86_64/kernel/domain.c create mode 100644 drivers/char/drm/drm_irq.h create mode 100644 drivers/char/drm/drm_pciids.h delete mode 100644 drivers/char/dz.c delete mode 100644 drivers/char/dz.h delete mode 100644 drivers/char/sh-sci.c delete mode 100644 drivers/char/sh-sci.h create mode 100644 drivers/char/watchdog/ixp4xx_wdt.c create mode 100644 drivers/dump/Makefile create mode 100644 drivers/dump/dump_arm.c create mode 100644 drivers/dump/dump_blockdev.c create mode 100644 drivers/dump/dump_execute.c create mode 100644 drivers/dump/dump_filters.c create mode 100644 drivers/dump/dump_fmt.c create mode 100644 drivers/dump/dump_gzip.c create mode 100644 drivers/dump/dump_i386.c create mode 100644 drivers/dump/dump_memdev.c create mode 100644 drivers/dump/dump_methods.h create mode 100644 drivers/dump/dump_netdev.c create mode 100644 drivers/dump/dump_overlay.c create mode 100644 drivers/dump/dump_ppc64.c create mode 100644 drivers/dump/dump_rle.c create mode 100644 drivers/dump/dump_scheme.c create mode 100644 drivers/dump/dump_setup.c delete mode 100644 drivers/i2c/busses/i2c-ixp42x.c create mode 100644 drivers/i2c/busses/i2c-ixp4xx.c create mode 100644 drivers/i2c/chips/max1619.c create mode 100644 drivers/i2c/chips/rtc8564.c create mode 100644 drivers/i2c/chips/rtc8564.h create mode 100644 drivers/ide/h8300/ide-h8300.c delete mode 100644 drivers/ide/ide-tcq.c delete mode 100644 drivers/ide/pci/alim15x3.h delete mode 100644 drivers/ide/pci/amd74xx.h delete mode 100644 drivers/ide/pci/cmd640.h delete mode 100644 drivers/ide/pci/cs5520.h delete mode 100644 drivers/ide/pci/cs5530.h delete mode 100644 drivers/ide/pci/ns87415.h delete mode 100644 drivers/ide/pci/rz1000.h delete mode 100644 drivers/ide/pci/sc1200.h delete mode 100644 drivers/ide/pci/siimage.h delete mode 100644 drivers/ide/pci/sis5513.h delete mode 100644 drivers/ide/pci/sl82c105.h delete mode 100644 drivers/ide/pci/slc90e66.h delete mode 100644 drivers/ide/pci/triflex.h delete mode 100644 drivers/ide/pci/trm290.h delete mode 100644 drivers/ide/pci/via82cxxx.h delete mode 100644 drivers/ide/ppc/swarm.c create mode 100644 drivers/mtd/maps/ixp4xx.c create mode 100644 drivers/mtd/maps/wr_sbc82xx_flash.c delete mode 100644 drivers/net/auto_irq.c create mode 100644 drivers/net/ibm_emac/ibm_emac.h create mode 100644 drivers/net/ibm_emac/ibm_emac_core.h create mode 100644 drivers/net/ibm_emac/ibm_emac_debug.c create mode 100644 drivers/net/ibm_emac/ibm_emac_mal.c create mode 100644 drivers/net/ibm_emac/ibm_emac_mal.h create mode 100644 drivers/net/ibm_emac/ibm_emac_phy.c create mode 100644 drivers/net/ibm_emac/ibm_emac_rgmii.h create mode 100644 drivers/net/ibm_emac/ibm_emac_tah.h create mode 100644 drivers/net/ibm_emac/ibm_emac_zmii.h create mode 100644 drivers/net/ne-h8300.c delete mode 100644 drivers/net/rcif.h delete mode 100644 drivers/net/rclanmtl.c delete mode 100644 drivers/net/rclanmtl.h delete mode 100644 drivers/net/rcpci45.c delete mode 100644 drivers/net/wan/comx-hw-comx.c delete mode 100644 drivers/net/wan/comx-hw-locomx.c delete mode 100644 drivers/net/wan/comx-hw-mixcom.c delete mode 100644 drivers/net/wan/comx-hw-munich.c delete mode 100644 drivers/net/wan/comx-proto-fr.c delete mode 100644 drivers/net/wan/comx-proto-lapb.c delete mode 100644 drivers/net/wan/comx-proto-ppp.c delete mode 100644 drivers/net/wan/comx.c delete mode 100644 drivers/net/wan/comx.h delete mode 100644 drivers/net/wan/comxhw.h delete mode 100644 drivers/net/wan/falc-lh.h delete mode 100644 drivers/net/wan/hscx.h delete mode 100644 drivers/net/wan/mixcom.h delete mode 100644 drivers/net/wan/munich32x.h delete mode 100644 drivers/net/wan/wanxlfw.inc create mode 100644 drivers/pcmcia/pxa2xx_base.h delete mode 100644 drivers/pcmcia/sa1100.h delete mode 100644 drivers/pcmcia/sa11xx_core.c delete mode 100644 drivers/pcmcia/sa11xx_core.h create mode 100644 drivers/scsi/ipr.c create mode 100644 drivers/scsi/ipr.h delete mode 100644 drivers/scsi/pcmcia/qlogic_core.c create mode 100644 drivers/scsi/pcmcia/sym53c500_cs.c rename drivers/scsi/{qlogicfas.h => qlogicfas408.h} (73%) create mode 100644 drivers/scsi/sata_promise.h rename drivers/usb/core/{driverfs.c => sysfs.c} (96%) create mode 100644 drivers/usb/input/touchkitusb.c create mode 100644 drivers/usb/misc/phidgetservo.c create mode 100644 drivers/video/asiliantfb.c create mode 100644 drivers/video/gbefb.c create mode 100644 drivers/video/pxafb.h delete mode 100644 fs/ext3/resize.c delete mode 100644 fs/hostfs/Makefile delete mode 100644 fs/hostfs/hostfs.h delete mode 100644 fs/hostfs/hostfs_kern.c delete mode 100644 fs/hostfs/hostfs_user.c delete mode 100644 fs/hppfs/Makefile delete mode 100644 fs/hppfs/hppfs_kern.c delete mode 100644 fs/intermezzo/Makefile delete mode 100644 fs/intermezzo/cache.c delete mode 100644 fs/intermezzo/dcache.c delete mode 100644 fs/intermezzo/dir.c delete mode 100644 fs/intermezzo/ext_attr.c delete mode 100644 fs/intermezzo/file.c delete mode 100644 fs/intermezzo/fileset.c delete mode 100644 fs/intermezzo/inode.c delete mode 100644 fs/intermezzo/intermezzo_fs.h delete mode 100644 fs/intermezzo/intermezzo_idl.h delete mode 100644 fs/intermezzo/intermezzo_journal.h delete mode 100644 fs/intermezzo/intermezzo_kml.h delete mode 100644 fs/intermezzo/intermezzo_lib.h delete mode 100644 fs/intermezzo/intermezzo_psdev.h delete mode 100644 fs/intermezzo/intermezzo_upcall.h delete mode 100644 fs/intermezzo/journal.c delete mode 100644 fs/intermezzo/journal_ext2.c delete mode 100644 fs/intermezzo/journal_ext3.c delete mode 100644 fs/intermezzo/journal_obdfs.c delete mode 100644 fs/intermezzo/journal_reiserfs.c delete mode 100644 fs/intermezzo/journal_tmpfs.c delete mode 100644 fs/intermezzo/journal_xfs.c delete mode 100644 fs/intermezzo/kml.c delete mode 100644 fs/intermezzo/kml_decode.c delete mode 100644 fs/intermezzo/kml_reint.c delete mode 100644 fs/intermezzo/kml_setup.c delete mode 100644 fs/intermezzo/kml_unpack.c delete mode 100644 fs/intermezzo/kml_utils.c delete mode 100644 fs/intermezzo/methods.c delete mode 100644 fs/intermezzo/presto.c delete mode 100644 fs/intermezzo/psdev.c delete mode 100644 fs/intermezzo/replicator.c delete mode 100644 fs/intermezzo/super.c delete mode 100644 fs/intermezzo/sysctl.c delete mode 100644 fs/intermezzo/upcall.c delete mode 100644 fs/intermezzo/vfs.c delete mode 100644 fs/rcfs/Makefile delete mode 100644 fs/rcfs/dir.c delete mode 100644 fs/rcfs/inode.c delete mode 100644 fs/rcfs/magic.c delete mode 100644 fs/rcfs/rootdir.c delete mode 100644 fs/rcfs/socket_fs.c delete mode 100644 fs/rcfs/super.c delete mode 100644 fs/rcfs/tc_magic.c create mode 100644 fs/reiserfs/xattr.c create mode 100644 fs/reiserfs/xattr_acl.c delete mode 100644 fs/relayfs/Makefile delete mode 100644 fs/relayfs/inode.c delete mode 100644 fs/relayfs/klog.c delete mode 100644 fs/relayfs/relay.c delete mode 100644 fs/relayfs/relay_locking.c delete mode 100644 fs/relayfs/relay_locking.h delete mode 100644 fs/relayfs/relay_lockless.c delete mode 100644 fs/relayfs/relay_lockless.h delete mode 100644 fs/relayfs/resize.c delete mode 100644 fs/relayfs/resize.h rename fs/xfs/{linux => linux-2.6}/kmem.h (91%) rename fs/xfs/{linux => linux-2.6}/mrlock.h (100%) rename fs/xfs/{linux => linux-2.6}/sema.h (100%) rename fs/xfs/{linux => linux-2.6}/sv.h (100%) rename fs/xfs/{linux => linux-2.6}/xfs_aops.c (96%) rename fs/xfs/{linux => linux-2.6}/xfs_buf.c (98%) rename fs/xfs/{linux => linux-2.6}/xfs_buf.h (100%) rename fs/xfs/{linux => linux-2.6}/xfs_file.c (99%) rename fs/xfs/{linux => linux-2.6}/xfs_fs_subr.c (100%) rename fs/xfs/{linux => linux-2.6}/xfs_globals.c (90%) rename fs/xfs/{linux => linux-2.6}/xfs_ioctl.c (97%) rename fs/xfs/{linux => linux-2.6}/xfs_iops.h (100%) rename fs/xfs/{linux => linux-2.6}/xfs_linux.h (96%) rename fs/xfs/{linux => linux-2.6}/xfs_lrw.c (99%) rename fs/xfs/{linux => linux-2.6}/xfs_lrw.h (100%) rename fs/xfs/{linux => linux-2.6}/xfs_stats.c (99%) rename fs/xfs/{linux => linux-2.6}/xfs_super.c (96%) rename fs/xfs/{linux => linux-2.6}/xfs_super.h (99%) rename fs/xfs/{linux => linux-2.6}/xfs_sysctl.c (91%) rename fs/xfs/{linux => linux-2.6}/xfs_sysctl.h (83%) rename fs/xfs/{linux => linux-2.6}/xfs_vfs.c (99%) rename fs/xfs/{linux => linux-2.6}/xfs_vfs.h (98%) rename fs/xfs/{linux => linux-2.6}/xfs_vnode.c (100%) rename fs/xfs/{linux => linux-2.6}/xfs_vnode.h (100%) delete mode 100644 fs/xfs/linux/mutex.h delete mode 100644 fs/xfs/linux/spin.h delete mode 100644 fs/xfs/linux/time.h delete mode 100644 fs/xfs/linux/xfs_cred.h delete mode 100644 fs/xfs/linux/xfs_fs_subr.h delete mode 100644 fs/xfs/linux/xfs_globals.h delete mode 100644 fs/xfs/linux/xfs_iops.c delete mode 100644 fs/xfs/linux/xfs_stats.h delete mode 100644 fs/xfs/linux/xfs_version.h delete mode 100644 include/asm-alpha/relay.h delete mode 100644 include/asm-alpha/rmap.h delete mode 100644 include/asm-arm/arch-cl7500/ide.h delete mode 100644 include/asm-arm/arch-cl7500/keyboard.h delete mode 100644 include/asm-arm/arch-clps711x/keyboard.h delete mode 100644 include/asm-arm/arch-ebsa110/ide.h delete mode 100644 include/asm-arm/arch-ebsa285/ide.h delete mode 100644 include/asm-arm/arch-iop3xx/ide.h create mode 100644 include/asm-arm/arch-ixp4xx/dma.h create mode 100644 include/asm-arm/arch-ixp4xx/io.h create mode 100644 include/asm-arm/arch-ixp4xx/irq.h create mode 100644 include/asm-arm/arch-ixp4xx/memory.h create mode 100644 include/asm-arm/arch-ixp4xx/param.h create mode 100644 include/asm-arm/arch-ixp4xx/platform.h create mode 100644 include/asm-arm/arch-ixp4xx/serial.h create mode 100644 include/asm-arm/arch-ixp4xx/system.h create mode 100644 include/asm-arm/arch-ixp4xx/time.h create mode 100644 include/asm-arm/arch-ixp4xx/uncompress.h delete mode 100644 include/asm-arm/arch-l7200/ide.h delete mode 100644 include/asm-arm/arch-l7200/keyboard.h delete mode 100644 include/asm-arm/arch-nexuspci/ide.h delete mode 100644 include/asm-arm/arch-omap/omap-h2.h delete mode 100644 include/asm-arm/arch-omap/omap-innovator.h delete mode 100644 include/asm-arm/arch-omap/omap-perseus2.h delete mode 100644 include/asm-arm/arch-pxa/ide.h delete mode 100644 include/asm-arm/arch-pxa/keyboard.h create mode 100644 include/asm-arm/arch-pxa/pxafb.h delete mode 100644 include/asm-arm/arch-rpc/ide.h delete mode 100644 include/asm-arm/arch-s3c2410/ide.h delete mode 100644 include/asm-arm/arch-sa1100/keyboard.h delete mode 100644 include/asm-arm/arch-shark/ide.h delete mode 100644 include/asm-arm/arch-shark/keyboard.h delete mode 100644 include/asm-arm/arch-tbox/ide.h delete mode 100644 include/asm-arm/relay.h delete mode 100644 include/asm-arm/rmap.h delete mode 100644 include/asm-arm26/relay.h delete mode 100644 include/asm-arm26/rmap.h delete mode 100644 include/asm-cris/relay.h delete mode 100644 include/asm-cris/rmap.h delete mode 100644 include/asm-generic/relay.h delete mode 100644 include/asm-generic/rmap.h delete mode 100644 include/asm-h8300/aki3068net/machine-depend.h delete mode 100644 include/asm-h8300/edosk2674/machine-depend.h delete mode 100644 include/asm-h8300/generic/machine-depend.h delete mode 100644 include/asm-h8300/generic/timer_rate.h delete mode 100644 include/asm-h8300/h8300_smsc.h delete mode 100644 include/asm-h8300/h8max/machine-depend.h delete mode 100644 include/asm-h8300/relay.h create mode 100644 include/asm-i386/dump.h delete mode 100644 include/asm-i386/relay.h delete mode 100644 include/asm-i386/rmap.h delete mode 100644 include/asm-ia64/relay.h delete mode 100644 include/asm-ia64/rmap.h delete mode 100644 include/asm-m68k/relay.h delete mode 100644 include/asm-m68k/rmap.h delete mode 100644 include/asm-m68knommu/relay.h delete mode 100644 include/asm-m68knommu/rmap.h delete mode 100644 include/asm-mips/relay.h delete mode 100644 include/asm-mips/rmap.h delete mode 100644 include/asm-mips64/relay.h delete mode 100644 include/asm-parisc/relay.h delete mode 100644 include/asm-parisc/rmap.h delete mode 100644 include/asm-ppc/relay.h delete mode 100644 include/asm-ppc/rmap.h delete mode 100644 include/asm-ppc64/relay.h delete mode 100644 include/asm-ppc64/rmap.h delete mode 100644 include/asm-s390/relay.h delete mode 100644 include/asm-s390/rmap.h delete mode 100644 include/asm-sh/relay.h delete mode 100644 include/asm-sh/rmap.h delete mode 100644 include/asm-sparc/relay.h delete mode 100644 include/asm-sparc/rmap.h delete mode 100644 include/asm-sparc64/relay.h delete mode 100644 include/asm-sparc64/rmap.h delete mode 100644 include/asm-um/cpufeature.h delete mode 100644 include/asm-um/local.h delete mode 100644 include/asm-um/module-generic.h delete mode 100644 include/asm-um/rmap.h delete mode 100644 include/asm-um/sections.h delete mode 100644 include/asm-v850/relay.h delete mode 100644 include/asm-v850/rmap.h delete mode 100644 include/asm-x86_64/relay.h delete mode 100644 include/asm-x86_64/rmap.h delete mode 100644 include/linux/ckrm.h delete mode 100644 include/linux/ckrm_ce.h delete mode 100644 include/linux/ckrm_net.h delete mode 100644 include/linux/ckrm_rc.h delete mode 100644 include/linux/ckrm_tc.h delete mode 100644 include/linux/ckrm_tsk.h create mode 100644 include/linux/dump.h create mode 100644 include/linux/dump_netdev.h create mode 100644 include/linux/dumpdev.h delete mode 100644 include/linux/klog.h create mode 100644 include/linux/mempolicy.h delete mode 100644 include/linux/rcfs.h create mode 100644 include/linux/reiserfs_acl.h create mode 100644 include/linux/reiserfs_xattr.h delete mode 100644 include/linux/relayfs_fs.h delete mode 100644 include/linux/taskdelays.h create mode 100644 init/kerntypes.c delete mode 100644 kernel/ckrm/Makefile delete mode 100644 kernel/ckrm/ckrm.c delete mode 100644 kernel/ckrm/ckrm_listenaq.c delete mode 100644 kernel/ckrm/ckrm_sockc.c delete mode 100644 kernel/ckrm/ckrm_tasks.c delete mode 100644 kernel/ckrm/ckrm_tc.c delete mode 100644 kernel/ckrm/ckrmutils.c delete mode 100644 net/bluetooth/syms.c create mode 100644 net/bridge/br_sysfs_br.c create mode 100644 scripts/checkstack.pl create mode 100644 scripts/reference_init.pl delete mode 100644 sound/pci/ice1712/prodigy.c delete mode 100644 sound/pci/ice1712/prodigy.h diff --git a/Documentation/COPYING.modules b/Documentation/COPYING.modules deleted file mode 100644 index da0266e78..000000000 --- a/Documentation/COPYING.modules +++ /dev/null @@ -1,708 +0,0 @@ -Date: Thu, 29 Apr 2004 14:10:41 -0700 (PDT) -From: Linus Torvalds -To: Giuliano Colla -cc: Linux Kernel Mailing List -Subject: Re: [hsflinux] [PATCH] Blacklist binary-only modules lying about - their license -Message-ID: - -On Thu, 29 Apr 2004, Giuliano Colla wrote: -> -> Let's try not to be ridiculous, please. - -It's not abotu being ridiculous. It's about honoring peoples copyrights. - -> As an end user, if I buy a full fledged modem, I get some amount of -> proprietary, non GPL, code which executes within the board or the -> PCMCIA card of the modem. The GPL driver may even support the -> functionality of downloading a new version of *proprietary* code into -> the flash Eprom of the device. The GPL linux driver interfaces with it, -> and all is kosher. - -Indeed. Everything is kosher, because the other piece of hardware and -software has _nothing_ to do with the kernel. It's not linked into it, it -cannot reasonably corrupt internal kernel data structures with random -pointer bugs, and in general you can think of firmware as part of the -_hardware_, not the software of the machine. - -> On the other hand, I have the misfortune of being stuck with a -> soft-modem, roughly the *same* proprietary code is provided as a binary -> file, and a linux driver (source provided) interfaces with it. In that -> case the kernel is flagged as "tainted". - -It is flagged as tainted, because your argument that it is "the same code" -is totally BOGUS AND UNTRUE! - -In the binary kernel module case, a bug in the code corrupts random data -structures, or accesses kernel internals without holding the proper locks, -or does a million other things wrong, BECAUSE A KERNEL MODULE IS VERY -INTIMATELY LINKED WITH THE KERNEL. - -A kernel module is _not_ a separate work, and can in _no_ way be seen as -"part of the hardware". It's very much a part of the _kernel_. And the -kernel developers require that such code be GPL'd so that it can be fixed, -or if there's a valid argument that it's not a derived work and not GPL'd, -then the kernel developers who have to support the end result mess most -definitely do need to know about the taint. - -You are not the first (and sadly, you likely won't be the last) person to -equate binary kernel modules with binary firmware. And I tell you that -such a comparison is ABSOLUTE CRAPOLA. There's a damn big difference -between running firmware on another chip behind a PCI bus, and linking -into the kernel directly. - -And if you don't see that difference, then you are either terminally -stupid, or you have some ulterior reason to claim that they are the same -case even though they clearly are NOT. - -> Can you honestly tell apart the two cases, if you don't make a it a case -> of "religion war"? - -It has absolutely nothing to do with religion. - - Linus - -Date: Fri, 5 Dec 2003 09:19:52 -0800 (PST) -From: Linus Torvalds -To: Peter Chubb -cc: linux-kernel@vger.kernel.org -Subject: Re: Linux GPL and binary module exception clause? -Message-ID: - -On Fri, 5 Dec 2003, Peter Chubb wrote: -> -> As I understand it, SCO is/was claiming that JFS and XFS are derived -> works of the UNIX source base, because they were developed to match -> the internal interfaces of UNIX, and with knowledge of the internals -> of UNIX -- and they hold the copyrights of and are the licensor of UNIX. - -Yes, and I'm not claiming anything like that. - -I claim that a "binary linux kernel module" is a derived work of the -kernel, and thus has to come with sources. - -But if you use those same sources (and _you_ wrote them) they do not -contain any Linux code, they are _clearly_ not derived from Linux, and you -can license and use your own code any way you want. - -You just can't make a binary module for Linux, and claim that that module -isn't derived from the kernel. Because it generally is - the binary -module not only included header files, but more importantly it clearly is -_not_ a standalone work any more. So even if you made your own prototypes -and tried hard to avoid kernel headers, it would _still_ be connected and -dependent on the kernel. - -And note that I'm very much talking about just the _binary_. Your source -code is still very much yours, and you have the right to distribute it -separately any which way you want. You wrote it, you own the copyrights to -it, and it is an independent work. - -But when you distribute it in a way that is CLEARLY tied to the GPL'd -kernel (and a binary module is just one such clear tie - a "patch" to -build it or otherwise tie it to the kernel is also such a tie, even if you -distribute it as source under some other license), you're BY DEFINITION -not an independent work any more. - -(But exactly because I'm not a black-and-white person, I reserve the right -to make a balanced decision on any particular case. I have several times -felt that the module author had a perfectly valid argument for why the -"default assumption" of being derived wasn't the case. That's why things -like the AFS module were accepted - but not liked - in the first place). - -This is why SCO's arguments are specious. IBM wrote their code, retained -their copyrights to their code AND THEY SEVERED THE CONNECTION TO SCO'S -CODE (and, arguably the connections didn't even exist in the first place, -since apparently things like JFS were written for OS/2 as well, and the -Linux port was based on that one - but that's a separate argument and -independent of my point). - -See the definition of "derivative" in USC 17.1.101: - - A "derivative work" is a work based upon one or more preexisting - works, such as a translation, musical arrangement, dramatization, - fictionalization, motion picture version, sound recording, art - reproduction, abridgment, condensation, or any other form in which - a work may be recast, transformed, or adapted. A work consisting - of editorial revisions, annotations, elaborations, or other - modifications which, as a whole, represent an original work of - authorship, is a "derivative work". - -And a binary module is an "elaboration" on the kernel. Sorry, but that is -how it IS. - -In short: your code is yours. The code you write is automatically -copyrighted by YOU, and as such you have the right to license and use it -any way you want (well, modulo _other_ laws, of course - in the US your -license can't be racist, for example, but that has nothing to do with -copyright laws, and would fall under a totally different legal framework). - -But when you use that code to create an "elaboration" to the kernel, that -makes it a derived work, and you cannot distribute it except as laid out -by the GPL. A binary module is one such case, but even just a source patch -is _also_ one such case. The lines you added are yours, but when you -distribute it as an elaboration, you are bound by the restriction on -derivative works. - -Or you had better have some other strong argument why it isn't. Which has -been my point all along. - - Linus - - -Date: Wed, 10 Dec 2003 09:10:18 -0800 (PST) -From: Linus Torvalds -To: Larry McVoy -Subject: Re: Linux GPL and binary module exception clause? - -On Wed, 10 Dec 2003, Larry McVoy wrote: -> -> Which is? How is it that you can spend a page of text saying a judge doesn't -> care about technicalities and then base the rest of your argument on the -> distinction between a "plugin" and a "kernel module"? - -I'll stop arguing, since you obviously do not get it. - -I explained the technicalities to _you_, and you are a technical person. - -But if you want to explain something to a judge, you get a real lawyer, -and you make sure that the lawyer tries to explain the issue in _non_ -technical terms. Because, quite frankly, the judge is not going to buy a -technical discussion he or she doesn't understand. - -Just as an example, how do you explain to a judge how much code the Linux -kernel contains? Do you say "it's 6 million lines of C code and header -files and documentation, for a total of about 175MB of data"? - -Yeah, maybe you'd _mention_ that, but to actually _illustrate_ the point -you'd say that if you printed it out, it would be a solid stack of papers -100 feet high. And you'd compare it to the height of the court building -you're in, or something. Maybe you'd print out _one_ file, bind it as a -book, and wave it around as one out of 15,000 files. - -But when _you_ ask me about how big the kernel is, I'd say "5 million -lines". See the difference? It would be silly for me to tell you how many -feet of paper the kernel would print out to, because we don't have those -kinds of associations. - -Similarly, if you want to explain the notion of a kernel module, you'd -compare it to maybe an extra chapter in a book. You'd make an analogy to -something that never _ever_ mentions "linking". - -Just imagine: distributing a compiled binary-only kernel module that can -be loaded into the kernel is not like distributing a new book: it's more -like distributing a extra chapter to a book that somebody else wrote, that -uses all the same characters and the plot, but more importantly it -literally can only be read _together_ with the original work. It doesn't -stand alone. - -In short, your honour, this extra chapter without any meaning on its own -is a derived work of the book. - -In contrast, maybe you can re-write your code and distribute it as a -short-story, which can be run on its own, and maybe the author has been -influenced by another book, but the short-story could be bound AS IS, and -a recipient would find it useful even without that other book. In that -case, the short story is not a derived work - it's only inspired. - -Notice? This is actually _exactly_ what I've been arguing all along, -except I've been arguing with a technical audience, so I've been using -technical examples and terminology. But my argument is that just the fact -that somebody compiled the code for Linux into a binary module that is -useless without a particular version of the kernel DOES MAKE IT A DERIVED -WORK. - -But also note how it's only the BINARY MODULE that is a derived work. Your -source code is _not_ necessarily a derived work, and if you compile it for -another operating system, I'd clearly not complain. - -This is the "stand-alone short story" vs "extra chapter without meaning -outside the book" argument. See? One is a work in its own right, the other -isn't. - - Linus - - -Please read the FAQ at http://www.tux.org/lkml/ -Date: Thu, 4 Dec 2003 22:43:42 -0800 (PST) -From: Linus Torvalds -To: David Schwartz -cc: linux-kernel@vger.kernel.org -Subject: RE: Linux GPL and binary module exception clause? - -On Thu, 4 Dec 2003, David Schwartz wrote: -> -> Yes, but they will cite the prohibition against *creating* derived -> works. - -So? - -The same prohibition exists with the GPL. You are not allowed to create -and distribute a derived work unless it is GPL'd. - -I don't see what you are arguing against. It is very clear: a kernel -module is a derived work of the kernel by default. End of story. - -You can then try to prove (through development history etc) that there -would be major reasons why it's not really derived. But your argument -seems to be that _nothing_ is derived, which is clearly totally false, as -you yourself admit when you replace "kernel" with "Harry Potter". - - Linus - -Date: Wed, 3 Dec 2003 16:00:21 -0800 (PST) -From: Linus Torvalds -To: Kendall Bennet -cc: linux-kernel@vger.kernel.org -Subject: Re: Linux GPL and binary module exception clause? - -On Wed, 3 Dec 2003, Kendall Bennett wrote: -> -> I have heard many people reference the fact that the although the Linux -> Kernel is under the GNU GPL license, that the code is licensed with an -> exception clause that says binary loadable modules do not have to be -> under the GPL. - -Nope. No such exception exists. - -There's a clarification that user-space programs that use the standard -system call interfaces aren't considered derived works, but even that -isn't an "exception" - it's just a statement of a border of what is -clearly considered a "derived work". User programs are _clearly_ not -derived works of the kernel, and as such whatever the kernel license is -just doesn't matter. - -And in fact, when it comes to modules, the GPL issue is exactly the same. -The kernel _is_ GPL. No ifs, buts and maybe's about it. As a result, -anything that is a derived work has to be GPL'd. It's that simple. - -Now, the "derived work" issue in copyright law is the only thing that -leads to any gray areas. There are areas that are not gray at all: user -space is clearly not a derived work, while kernel patches clearly _are_ -derived works. - -But one gray area in particular is something like a driver that was -originally written for another operating system (ie clearly not a derived -work of Linux in origin). At exactly what point does it become a derived -work of the kernel (and thus fall under the GPL)? - -THAT is a gray area, and _that_ is the area where I personally believe -that some modules may be considered to not be derived works simply because -they weren't designed for Linux and don't depend on any special Linux -behaviour. - -Basically: - - anything that was written with Linux in mind (whether it then _also_ - works on other operating systems or not) is clearly partially a derived - work. - - anything that has knowledge of and plays with fundamental internal - Linux behaviour is clearly a derived work. If you need to muck around - with core code, you're derived, no question about it. - -Historically, there's been things like the original Andrew filesystem -module: a standard filesystem that really wasn't written for Linux in the -first place, and just implements a UNIX filesystem. Is that derived just -because it got ported to Linux that had a reasonably similar VFS interface -to what other UNIXes did? Personally, I didn't feel that I could make that -judgment call. Maybe it was, maybe it wasn't, but it clearly is a gray -area. - -Personally, I think that case wasn't a derived work, and I was willing to -tell the AFS guys so. - -Does that mean that any kernel module is automatically not a derived work? -HELL NO! It has nothing to do with modules per se, except that non-modules -clearly are derived works (if they are so central to the kenrel that you -can't load them as a module, they are clearly derived works just by virtue -of being very intimate - and because the GPL expressly mentions linking). - -So being a module is not a sign of not being a derived work. It's just -one sign that _maybe_ it might have other arguments for why it isn't -derived. - - Linus - - -Date: Wed, 3 Dec 2003 16:23:33 -0800 (PST) -From: Linus Torvalds -To: Kendall Bennett -cc: linux-kernel@vger.kernel.org -Subject: Re: Linux GPL and binary module exception clause? - - -On Wed, 3 Dec 2003, Linus Torvalds wrote: -> -> So being a module is not a sign of not being a derived work. It's just -> one sign that _maybe_ it might have other arguments for why it isn't -> derived. - -Side note: historically, the Linux kernel module interfaces were really -quite weak, and only exported a few tens of entry-points, and really -mostly effectively only allowed character and block device drivers with -standard interfaces, and loadable filesystems. - -So historically, the fact that you could load a module using nothing but -these standard interfaces tended to be a much stronger argument for not -being very tightly coupled with the kernel. - -That has changed, and the kernel module interfaces we have today are MUCH -more extensive than they were back in '95 or so. These days modules are -used for pretty much everything, including stuff that is very much -"internal kernel" stuff and as a result the kind of historic "implied -barrier" part of modules really has weakened, and as a result there is not -avery strong argument for being an independent work from just the fact -that you're a module. - -Similarly, historically there was a much stronger argument for things like -AFS and some of the binary drivers (long forgotten now) for having been -developed totally independently of Linux: they literally were developed -before Linux even existed, by people who had zero knowledge of Linux. That -tends to strengthen the argument that they clearly aren't derived. - -In contrast, these days it would be hard to argue that a new driver or -filesystem was developed without any thought of Linux. I think the NVidia -people can probably reasonably honestly say that the code they ported had -_no_ Linux origin. But quite frankly, I'd be less inclined to believe that -for some other projects out there.. - - Linus - - - - -Date: Thu, 17 Oct 2002 10:08:19 -0700 (PDT) -From: Linus Torvalds -To: Christoph Hellwig -Cc: -Subject: Re: [PATCH] make LSM register functions GPLonly exports -In-Reply-To: <20021017175403.A32516@infradead.org> -Message-ID: - -Note that if this fight ends up being a major issue, I'm just going to -remove LSM and let the security vendors do their own thing. So far - - - I have not seen a lot of actual usage of the hooks - - seen a number of people who still worry that the hooks degrade - performance in critical areas - - the worry that people use it for non-GPL'd modules is apparently real, - considering Crispin's reply. - -I will re-iterate my stance on the GPL and kernel modules: - - There is NOTHING in the kernel license that allows modules to be - non-GPL'd. - - The _only_ thing that allows for non-GPL modules is copyright law, and - in particular the "derived work" issue. A vendor who distributes non-GPL - modules is _not_ protected by the module interface per se, and should - feel very confident that they can show in a court of law that the code - is not derived. - - The module interface has NEVER been documented or meant to be a GPL - barrier. The COPYING clearly states that the system call layer is such a - barrier, so if you do your work in user land you're not in any way - beholden to the GPL. The module interfaces are not system calls: there - are system calls used to _install_ them, but the actual interfaces are - not. - - The original binary-only modules were for things that were pre-existing - works of code, ie drivers and filesystems ported from other operating - systems, which thus could clearly be argued to not be derived works, and - the original limited export table also acted somewhat as a barrier to - show a level of distance. - -In short, Crispin: I'm going to apply the patch, and if you as a copyright -holder of that file disagree, I will simply remove all of he LSM code from -the kernel. I think it's very clear that a LSM module is a derived work, -and thus copyright law and the GPL are not in any way unclear about it. - -If people think they can avoid the GPL by using function pointers, they -are WRONG. And they have always been wrong. - - Linus - ------------------------------------------------------------------------- -Date: Fri, 19 Oct 2001 13:16:45 -0700 (PDT) -From: Linus Torvalds -To: Barnes -Subject: Re: GPL, Richard Stallman, and the Linux kernel - -[ This is not, of course, a legal document, but if you want to forward it - to anybody else, feel free to do so. And if you want to argue legal - points with me or point somehting out, I'm always interested. To a - point ;-] - -On Fri, 19 Oct 2001, Barnes wrote: -> -> I've been exchanging e-mail with Richard Stallman for a couple of -> weeks about the finer points of the GPL. - -I feel your pain. - -> I've have spent time pouring through mailing list archives, usenet, -> and web search engines to find out what's already been covered about -> your statement of allowing dynamically loaded kernel modules with -> proprietary code to co-exist with the Linux kernel. So far I've -> been unable to find anything beyond vague statements attributed to -> you. If these issues are addressed somewhere already, please refer -> me. - -Well, it really boils down to the equivalent of "_all_ derived modules -have to be GPL'd". An external module doesn't really change the GPL in -that respect. - -There are (mainly historical) examples of UNIX device drivers and some -UNIX filesystems that were pre-existing pieces of work, and which had -fairly well-defined and clear interfaces and that I personally could not -really consider any kind of "derived work" at all, and that were thus -acceptable. The clearest example of this is probably the AFS (the Andrew -Filesystem), but there have been various device drivers ported from SCO -too. - -> Issue #1 -> ======== -> Currently the GPL version 2 license is the only license covering the -> Linux kernel. I cannot find any alternative license explaining the -> loadable kernel module exception which makes your position difficult -> to legally analyze. -> -> There is a note at the top of www.kernel.org/pub/linux/kernel/COPYING, -> but that states "user programs" which would clearly not apply to -> kernel modules. -> -> Could you clarify in writing what the exception precisely states? - -Well, there really is no exception. However, copyright law obviously -hinges on the definition of "derived work", and as such anything can -always be argued on that point. - -I personally consider anything a "derived work" that needs special hooks -in the kernel to function with Linux (ie it is _not_ acceptable to make a -small piece of GPL-code as a hook for the larger piece), as that obviously -implies that the bigger module needs "help" from the main kernel. - -Similarly, I consider anything that has intimate knowledge about kernel -internals to be a derived work. - -What is left in the gray area tends to be clearly separate modules: code -that had a life outside Linux from the beginning, and that do something -self-containted that doesn't really have any impact on the rest of the -kernel. A device driver that was originally written for something else, -and that doesn't need any but the standard UNIX read/write kind of -interfaces, for example. - -> Issue #2 -> ======== -> I've found statements attributed to you that you think only 10% of -> the code in the current kernel was written by you. By not being the -> sole copyright holder of the Linux kernel, a stated exception to -> the GPL seems invalid unless all kernel copyright holders agreed on -> this exception. How does the exception cover GPL'd kernel code not -> written by you? Has everyone contributing to the kernel forfeited -> their copyright to you or agreed with the exception? - -Well, see above about the lack of exception, and about the fundamental -gray area in _any_ copyright issue. The "derived work" issue is obviously -a gray area, and I know lawyers don't like them. Crazy people (even -judges) have, as we know, claimed that even obvious spoofs of a work that -contain nothing of the original work itself, can be ruled to be "derived". - -I don't hold views that extreme, but at the same time I do consider a -module written for Linux and using kernel infrastructures to get its work -done, even if not actually copying any existing Linux code, to be a -derived work by default. You'd have to have a strong case to _not_ -consider your code a derived work.. - -> Issue #3 -> ======== -> This issue is related to issue #1. Exactly what is covered by the -> exception? For example, all code shipped with the Linux kernel -> archive and typically installed under /usr/src/linux, all code under -> /usr/src/linux except /usr/src/linux/drivers, or just the code in -> the /usr/src/linux/kernel directory? - -See above, and I think you'll see my point. - -The "user program" exception is not an exception at all, for example, it's -just a more clearly stated limitation on the "derived work" issue. If you -use standard UNIX system calls (with accepted Linux extensions), your -program obviously doesn't "derive" from the kernel itself. - -Whenever you link into the kernel, either directly or through a module, -the case is just a _lot_ more muddy. But as stated, by default it's -obviously derived - the very fact that you _need_ to do something as -fundamental as linking against the kernel very much argues that your -module is not a stand-alone thing, regardless of where the module source -code itself has come from. - -> Issue #4 -> ======== -> This last issue is not so much a issue for the Linux kernel -> exception, but a request for comment. -> -> Richard and I both agree that a "plug-in" and a "dynamically -> loaded kernel module" are effectively the same under the GPL. - -Agreed. - -The Linux kernel modules had (a long time ago), a more limited interface, -and not very many functions were actually exported. So five or six years -ago, we could believably claim that "if you only use these N interfaces -that are exported from the standard kernel, you've kind of implicitly -proven that you do not need the kernel infrastructure". - -That was never really documented either (more of a guideline for me and -others when we looked at the "derived work" issue), and as modules were -more-and-more used not for external stuff, but just for dynamic loading of -standard linux modules that were distributed as part of the kernel anyway, -the "limited interfaces" argument is no longer a very good guideline for -"derived work". - -So these days, we export many internal interfaces, not because we don't -think that they would "taint" the linker, but simply because it's useful -to do dynamic run-time loading of modules even with standard kernel -modules that _are_ supposed to know a lot about kernel internals, and are -obviously "derived works".. - -> However we disagree that a plug-in for a GPL'd program falls -> under the GPL as asserted in the GPL FAQ found in the answer: -> http://www.gnu.org/licenses/gpl-faq.html#GPLAndPlugins. - -I think you really just disagree on what is derived, and what is not. -Richard is very extreme: _anything_ that links is derived, regardless of -what the arguments against it are. I'm less extreme, and I bet you're even -less so (at least you would like to argue so for your company). - -> My assertion is that plug-ins are written to an interface, not a -> program. Since interfaces are not GPL'd, a plug-in cannot be GPL'd -> until the plug-in and program are placed together and run. That is -> done by the end user, not the plug-in creator. - -I agree, but also disrespectfully disagree ;) - -It's an issue of what a "plug-in" is - is it a way for the program to -internally load more modules as it needs them, or is it _meant_ to be a -public, published interface. - -For example, the "system call" interface could be considered a "plug-in -interface", and running a user mode program under Linux could easily be -construed as running a "plug-in" for the Linux kernel. No? - -And there, I obviously absolutely agree with you 100%: the interface is -published, and it's _meant_ for external and independent users. It's an -interface that we go to great lengths to preserve as well as we can, and -it's an interface that is designed to be independent of kernel versions. - -But maybe somebody wrote his program with the intention to dynamically -load "actors" as they were needed, as a way to maintain a good modularity, -and to try to keep the problem spaces well-defined. In that case, the -"plug-in" may technically follow all the same rules as the system call -interface, even though the author doesn't intend it that way. - -So I think it's to a large degree a matter of intent, but it could -arguably also be considered a matter of stability and documentation (ie -"require recompilation of the plug-in between version changes" would tend -to imply that it's an internal interface, while "documented binary -compatibility across many releases" implies a more stable external -interface, and less of a derived work) - -Does that make sense to you? - -> I asked Richard to comment on several scenarios involving plug-ins -> explain whether or not they were in violation of the GPL. So far he -> as only addressed one and has effectively admitted a hole. This is -> the one I asked that he's responded to: -> [A] non-GPL'd plug-in writer writes a plug-in for a non-GPL'd -> program. Another author writes a GPL'd program making the -> first author's plug-ins compatible with his program. Are now -> the plug-in author's plug-ins now retroactively required to be -> GPL'd? -> -> His response: -> No, because the plug-in was not written to extend this program. -> -> I find it suspicious that whether or not the GPL would apply to the -> plug-in depends on the mindset of the author. - -The above makes no sense if you think of it as a "plug in" issue, but it -makes sense if you think of it as a "derived work" issue, along with -taking "intent" into account. - -I know lawyers tend to not like the notion of "intent", because it brings -in another whole range of gray areas, but it's obviously a legal reality. - -Ok, enough blathering from me. I'd just like to finish off with a few -comments, just to clarify my personal stand: - - - I'm obviously not the only copyright holder of Linux, and I did so on - purpose for several reasons. One reason is just because I hate the - paperwork and other cr*p that goes along with copyright assignments. - - Another is that I don't much like copyright assignments at all: the - author is the author, and he may be bound by my requirement for GPL, - but that doesn't mean that he should give his copyright to me. - - A third reason, and the most relevant reason here, is that I want - people to _know_ that I cannot control the sources. I can write you a - note to say that "for use XXX, I do not consider module YYY to be a - derived work of my kernel", but that would not really matter that much. - Any other Linux copyright holder might still sue you. - - This third reason is what makes people who otherwise might not trust me - realize that I cannot screw people over. I am bound by the same - agreement that I require of everybody else, and the only special status - I really have is a totally non-legal issue: people trust me. - - (Yes, I realize that I probably would end up having more legal status - than most, even apart from the fact that I still am the largest single - copyright holder, if only because of appearances) - - - I don't really care about copyright law itself. What I care about is my - own morals. Whether I'd ever sue somebody or not (and quite frankly, - it's the last thing I ever want to do - if I never end up talking to - lawyers in a professional context, I'll be perfectly happy. No - disrespect intended) will be entirely up to whether I consider what - people do to me "moral" or not. Which is why intent matters to me a - lot - both the intent of the person/corporation doign the infringement, - _and_ the intent of me and others in issues like the module export - interface. - - Another way of putting this: I don't care about "legal loopholes" and - word-wrangling. - - - Finally: I don't trust the FSF. I like the GPL a lot - although not - necessarily as a legal piece of paper, but more as an intent. Which - explains why, if you've looked at the Linux COPYING file, you may have - noticed the explicit comment about "only _this_ particular version of - the GPL covers the kernel by default". - - That's because I agree with the GPL as-is, but I do not agree with the - FSF on many other matters. I don't like software patents much, for - example, but I do not want the code I write to be used as a weapon - against companies that have them. The FSF has long been discussing and - is drafting the "next generation" GPL, and they generally suggest that - people using the GPL should say "v2 or at your choice any later - version". - - Linux doesn't do that. The Linux kernel is v2 ONLY, apart from a few - files where the author put in the FSF extension (and see above about - copyright assignments why I would never remove such an extension). - -The "v2 only" issue might change some day, but only after all documented -copyright holders agree on it, and only after we've seen what the FSF -suggests. From what I've seen so far from the FSF drafts, we're not likely -to change our v2-only stance, but there might of course be legal reasons -why we'd have to do something like it (ie somebody challenging the GPLv2 -in court, and part of it to be found unenforceable or similar would -obviously mean that we'd have to reconsider the license). - - Linus - -PS. Historically, binary-only modules have not worked well under Linux, -quite regardless of any copyright issues. The kernel just develops too -quickly for binary modules to work well, and nobody really supports them. -Companies like Red Hat etc tend to refuse to have anything to do with -binary modules, because if something goes wrong there is nothing they can -do about it. So I just wanted to let you know that the _legal_ issue is -just the beginning. Even though you probably don't personally care ;) - - diff --git a/Documentation/arm/IXP4xx b/Documentation/arm/IXP4xx new file mode 100644 index 000000000..d86d818a4 --- /dev/null +++ b/Documentation/arm/IXP4xx @@ -0,0 +1,155 @@ + +------------------------------------------------------------------------- +Release Notes for Linux on Intel's IXP4xx Network Processor + +Maintained by Deepak Saxena +------------------------------------------------------------------------- + +1. Overview + +Intel's IXP4xx network processor is a highly integrated SOC that +is targeted for network applications, though it has become popular +in industrial control and other areas due to low cost and power +consumption. The IXP4xx family currently consists of several processors +that support different network offload functions such as encryption, +routing, firewalling, etc. For more information on the various +versions of the CPU, see: + + http://developer.intel.com/design/network/products/npfamily/ixp4xx.htm + +Intel also made the IXCP1100 CPU for sometime which is an IXP4xx +stripped of much of the network intelligence. + +2. Linux Support + +Linux currently supports the following features on the IXP4xx chips: + +- Dual serial ports +- PCI interface +- Flash access (MTD/JFFS) +- I2C through GPIO +- GPIO for input/output/interrupts + See include/asm-arm/arch-ixp4xx/platform.h for access functions. +- Timers (watchdog, OS) + +The following components of the chips are not supported by Linux and +require the use of Intel's propietary CSR softare: + +- USB device interface +- Network interfaces (HSS, Utopia, NPEs, etc) +- Network offload functionality + +If you need to use any of the above, you need to download Intel's +software from: + + http://developer.intel.com/design/network/products/npfamily/ixp425swr1.htm + +DO NOT POST QUESTIONS TO THE LINUX MAILING LISTS REGARDING THE PROPIETARY +SOFTWARE. + +There are several websites that provide directions/pointers on using +Intel's software: + +http://ixp4xx-osdg.sourceforge.net/ + Open Source Developer's Guide for using uClinux and the Intel libraries + +http://gatewaymaker.sourceforge.net/ + Simple one page summary of building a gateway using an IXP425 and Linux + +http://ixp425.sourceforge.net/ + ATM device driver for IXP425 that relies on Intel's libraries + +3. Known Issues/Limitations + +3a. Limited inbound PCI window + +The IXP4xx family allows for up to 256MB of memory but the PCI interface +can only expose 64MB of that memory to the PCI bus. This means that if +you are running with > 64MB, all PCI buffers outside of the accessible +range will be bounced using the routines in arch/arm/common/dmabounce.c. + +3b. Limited outbound PCI window + +IXP4xx provides two methods of accessing PCI memory space: + +1) A direct mapped window from 0x48000000 to 0x4bffffff (64MB). + To access PCI via this space, we simply ioremap() the BAR + into the kernel and we can use the standard read[bwl]/write[bwl] + macros. This is the preffered method due to speed but it + limits the system to just 64MB of PCI memory. This can be + problamatic if using video cards and other memory-heavy devices. + +2) If > 64MB of memory space is required, the IXP4xx can be + configured to use indirect registers to access PCI This allows + for up to 128MB (0x48000000 to 0x4fffffff) of memory on the bus. + The disadvantadge of this is that every PCI access requires + three local register accesses plus a spinlock, but in some + cases the performance hit is acceptable. In addition, you cannot + mmap() PCI devices in this case due to the indirect nature + of the PCI window. + +By default, the direct method is used for performance reasons. If +you need more PCI memory, enable the IXP4XX_INDIRECT_PCI config option. + +3c. GPIO as Interrupts + +Currently the code only handles level-sensitive GPIO interrupts + +4. Supported platforms + +ADI Engineering Coyote Gateway Reference Platform +http://www.adiengineering.com/productsCoyote.html + + The ADI Coyote platform is reference design for those building + small residential/office gateways. One NPE is connected to a 10/100 + interface, one to 4-port 10/100 switch, and the third to and ADSL + interface. In addition, it also supports to POTs interfaces connected + via SLICs. Note that those are not supported by Linux ATM. Finally, + the platform has two mini-PCI slots used for 802.11[bga] cards. + Finally, there is an IDE port hanging off the expansion bus. + +Gateworks Avila Network Platform +http://www.gateworks.com/avila_sbc.htm + + The Avila platform is basically and IXDP425 with the 4 PCI slots + replaced with mini-PCI slots and a CF IDE interface hanging off + the expansion bus. + +Intel IXDP425 Development Platform +http://developer.intel.com/design/network/products/npfamily/ixdp425.htm + + This is Intel's standard reference platform for the IXDP425 and is + also known as the Richfield board. It contains 4 PCI slots, 16MB + of flash, two 10/100 ports and one ADSL port. + +Motorola PrPMC1100 Processor Mezanine Card +http://www.fountainsys.com/datasheet/PrPMC1100.pdf + + The PrPMC1100 is based on the IXCP1100 and is meant to plug into + and IXP2400/2800 system to act as the system controller. It simply + contains a CPU and 16MB of flash on the board and needs to be + plugged into a carrier board to function. Currently Linux only + supports the Motorola PrPMC carrier board for this platform. + See https://mcg.motorola.com/us/ds/pdf/ds0144.pdf for info + on the carrier board. + +5. TODO LIST + +- Add support for Coyote IDE +- Add support for edge-based GPIO interrupts +- Add support for CF IDE on expansion bus + +6. Thanks + +The IXP4xx work has been funded by Intel Corp. and MontaVista Software, Inc. + +The following people have contributed patches/comments/etc: + +Lutz Jaenicke +Justin Mayfield +Robert E. Ranslam +[I know I've forgotten others, please email me to be added] + +------------------------------------------------------------------------- + +Last Update: 5/13/2004 diff --git a/Documentation/arm/SA1100/PCMCIA b/Documentation/arm/SA1100/PCMCIA deleted file mode 100644 index 5eb5d3ab3..000000000 --- a/Documentation/arm/SA1100/PCMCIA +++ /dev/null @@ -1,374 +0,0 @@ -Kernel Low-Level PCMCIA Interface Documentation -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -John G Dorsey -Updated: 30 June, 2000 - - -Note: this interface has not been finalized! -See also: http://www.cs.cmu.edu/~wearable/software/pcmcia-arm.html - - -Introduction - -Early versions of PCMCIA Card Services for StrongARM were designed to -permit a single socket driver to run on a variety of SA-1100 boards by -using a userland configuration process. During the conversion to the 2.3 -kernel series, all of the configuration has moved into sub-drivers in the -kernel proper (see linux/drivers/pcmcia/sa1100*). This document describes -the low-level interface between those sub-drivers and the sa1100 socket -driver module. - -Presently, there are six operations which must be provided by the -board-specific code. Only functions whose implementation is likely to -differ across board designs are required at this level. Some examples -include: - - - configuring card detect lines to generate interrupts - - sensing the legal voltage levels for inserted cards - - asserting the reset signal for a card - -Functions which are assumed to be the same across all designs are -performed within the generic socket driver itself. Some examples of these -kinds of operations include: - - - configuring memory access times based on the core clock frequency - - reads/writes on memory, byte swizzling, ... - -The current implementation allows the specific per-board set of low-level -operations to be determined at run time. For each specific board, the -following structure should be filled in: - - struct pcmcia_low_level { - int (*init)(struct pcmcia_init *); - int (*shutdown)(void); - int (*socket_state)(struct pcmcia_state_array *); - int (*get_irq_info)(struct pcmcia_irq_info *); - int (*configure_socket)(const struct pcmcia_configure *); - }; - -The component functions are described in detail below. Using the -machine_is_*() tests, the pointer `pcmcia_low_level' should be assigned to -the location of the table for your board. - - -0. init(struct pcmcia_init *init) - -This operation has three responsibilities: - - - perform any board-specific initialization tasks - - associate the given handler with any interrupt-generating signals - such as card detection, or battery voltage detection - - set up any necessary edge detection for card ready signals - -Argument passing for this operation is implemented by the following -structure: - - struct pcmcia_init { - void (*handler)(int irq, void *dev, struct pt_regs *regs); - struct pcmcia_maps *maps; - }; - -Here, `handler' is provided by the socket driver, and `maps' must be -modified if the default mapping isn't appropriate. This operation should -return one of two values: - - - the highest-numbered socket available, plus one - - a negative number, indicating an error in configuration - -Note that the former case is _not_ the same as "the number of sockets -available." In particular, if your design uses SA-1100 slot "one" but -not slot "zero," you MUST report "2" to the socket driver. - - -1. shutdown(void) - -This operation takes no arguments, and will be called during cleanup for -the socket driver module. Any state associated with the socket controller, -including allocated data structures, reserved IRQs, etc. should be -released in this routine. - -The return value for this operation is not examined. - - -2. socket_state(struct pcmcia_state_array *state_array) - -This operation will be invoked from the interrupt handler which was set up -in the earlier call to init(). Note, however, that it should not include -any side effects which would be inappropriate if the operation were to -occur when no interrupt is pending. (An extra invocation of this operation -currently takes place to initialize state in the socket driver.) - -Argument passing for this operation is handled by a structure which -contains an array of the following type: - - struct pcmcia_state { - unsigned detect: 1, - ready: 1, - bvd1: 1, - bvd2: 1, - wrprot: 1, - vs_3v: 1, - vs_Xv: 1; - }; - -Upon return from the operation, a struct pcmcia_state should be filled in -for each socket available in the hardware. For every array element (up to -`size' in the struct pcmcia_state_saarray) which does not correspond to an -available socket, zero the element bits. (This includes element [0] if -socket zero is not used.) - -Regardless of how the various signals are routed to the SA-1100, the bits -in struct pcmcia_state always have the following semantics: - - detect - 1 if a card is fully inserted, 0 otherwise - ready - 1 if the card ready signal is asserted, 0 otherwise - bvd1 - the value of the Battery Voltage Detect 1 signal - bvd2 - the value of the Battery Voltage Detect 2 signal - wrprot - 1 if the card is write-protected, 0 otherwise - vs_3v - 1 if the card must be operated at 3.3V, 0 otherwise - vs_Xv - 1 if the card must be operated at X.XV, 0 otherwise - -A note about the BVD signals: if your board does not make both lines -directly observable to the processor, just return reasonable values. The -standard interpretation of the BVD signals is: - - BVD1 BVD2 - - 0 x battery is dead - 1 0 battery warning - 1 1 battery ok - -Regarding the voltage sense flags (vs_3v, vs_Xv), these bits should be set -based on a sampling of the Voltage Sense pins, if available. The standard -interpretation of the VS signals (for a "low-voltage" socket) is: - - VS1 VS2 - - 0 0 X.XV, else 3.3V, else none - 0 1 3.3V, else none - 1 0 X.XV, else none - 1 1 5V, else none - -More information about the BVD and VS conventions is available in chapter -5 of "PCMCIA System Architecture," 2nd ed., by Don Anderson. - -This operation should return 1 if an IRQ is actually pending for the -socket controller, 0 if no IRQ is pending (but no error condition exists, -such as an undersized state array), or -1 on any error. - - -3. get_irq_info(struct pcmcia_irq_info *info) - -This operation obtains the IRQ assignment which is legal for the given -socket. An argument of the following type is passed: - - struct pcmcia_irq_info { - unsigned int sock; - unsigned int irq ; - }; - -The `sock' field contains the socket index being queried. The `irq' field -should contain the IRQ number corresponding to the card ready signal from -the device. - -This operation should return 0 on success, or -1 on any error. - - -4. configure_socket(const struct pcmcia_configure *configure) - -This operation allows the caller to apply power to the socket, issue a -reset, or enable various outputs. The argument is of the following type: - - struct pcmcia_configure { - unsigned sock: 8, - vcc: 8, - vpp: 8, - output: 1, - speaker: 1, - reset: 1; - }; - -The `sock' field contains the index of the socket to be configured. The -`vcc' and `vpp' fields contain the voltages to be applied for Vcc and Vpp, -respectively, in units of 0.1V. (Note that vpp==120 indicates that -programming voltage should be applied.) - -The two output enables, `output' and `speaker', refer to the card data -signal enable and the card speaker enable, respectively. The `reset' bit, -when set, indicates that the card reset should be asserted. - -This operation should return 0 on success, or -1 on any error. - - -Board-Specific Notes - -The following information is known about various SA-11x0 board designs -which may be used as reference while adding support to the kernel. - - -Carnegie Mellon Itsy/Cue (http://www.cs.cmu.edu/~wearable/itsy/) - - Itsy Chip Select 3 (CS3) Interface - ("ITSY MEMORY/PCMCIA ADD-ON BOARD with BATTERY and CHARGER CIRCUITRY," - memo dated 5-20-99, from Tim Manns to Richard Martin, et. al) - - Read: - ABVD2 (SS)D0 A slot, Battery Voltage Detect - ABVD1 (SS)D1 - AVSS2 (SS)D2 A slot, Voltage Sense - AVSS1 (SS)D3 - GND (SS)D4 - GND (SS)D5 - GND (SS)D6 - GND (SS)D7 - - BBVD2 (SS)D8 B slot, Battery Voltage Detect - BBVD1 (SS)D9 - BVSS2 (SS)D10 B slot, Voltage Sense - BVSS1 (SS)D11 - GND (SS)D12 - GND (SS)D13 - GND (SS)D14 - GND (SS)D15 - - Write: - (SS)D0 A_VPP_VCC LTC1472 VPPEN1 - (SS)D1 A_VPP_PGM LTC1472 VPPEN0 - (SS)D2 A_VCC_3 LTC1472 VCCEN0 - (SS)D3 A_VCC_5 LTC1472 VCCEN1 - (SS)D4 RESET (A SLOT) - (SS)D5 GND - (SS)D6 GND - (SS)D7 GND - - (SS)D8 B_VPP_VCC LTC1472 VPPEN1 - (SS)D9 B_VPP_PGM LTC1472 VPPEN0 - (SS)D10 B_VCC_3 LTC1472 VCCEN0 - (SS)D11 B_VCC_5 LTC1472 VCCEN1 - (SS)D12 RESET (B SLOT) - (SS)D13 GND - (SS)D14 GND - (SS)D15 GND - - GPIO pin assignments are as follows: (from schematics) - - GPIO 10 Slot 0 Card Detect - GPIO 11 Slot 1 Card Detect - GPIO 12 Slot 0 Ready/Interrupt - GPIO 13 Slot 1 Ready/Interrupt - - - -Intel SA-1100 Multimedia Board (http://developer.intel.com/design/strong/) - - CPLD Registers - SA-1100 Multimedia Development Board with Companion SA-1101 Development - Board User's Guide, p.4-42 - - This SA-1100/1101 development package uses only one GPIO pin (24) to - signal changes in card status, and requires software to inspect a - PCMCIA status register to determine the source. - - Read: (PCMCIA Power Sense Register - 0x19400000) - S0VS1 0 Slot 0 voltage sense - S0VS2 1 - S0BVD1 2 Slot 0 battery voltage sense - S0BVD2 3 - S1VS1 4 Slot 1 voltage sense - S1VS2 5 - S1BVD1 6 Slot 1 battery voltage sense - S1BVD2 7 - - Read/Write: (PCMCIA Power Control Register - 0x19400002) - S0VPP0 0 Slot 0 Vpp - S0VPP1 1 - S0VCC0 2 Slot 0 Vcc - S0VCC1 3 - S1VPP0 4 Slot 1 Vpp - S1VPP1 5 - S1VCC0 6 Slot 1 Vcc - S1VCC1 7 - - Read: (PCMCIA Status Register - 0x19400004) - S0CD1 0 Slot 0 Card Detect 1 - S0RDY 1 Slot 0 Ready/Interrupt - S0STSCHG 2 Slot 0 Status Change - S0Reset 3 Slot 0 Reset (RW) - S1CD1 4 Slot 1 Card Detect 1 - S1RDY 5 Slot 1 Ready/Interrupt - S1STSCHG 6 Slot 1 Status Change - S1Reset 7 Slot 1 Reset (RW) - - - -Intel SA-1100 Evaluation Platform (http://developer.intel.com/design/strong/) - - Brutus I/O Pins and Chipselect Register - pcmcia-brutus.c, by Ivo Clarysse - (What's the official reference for this info?) - - This SA-1100 development board uses more GPIO pins than say, the Itsy - or the SA-1100/1101 multimedia package. The pin assignments are as - follows: - - GPIO 2 Slot 0 Battery Voltage Detect 1 - GPIO 3 Slot 0 Ready/Interrupt - GPIO 4 Slot 0 Card Detect - GPIO 5 Slot 1 Battery Voltage Detect 1 - GPIO 6 Slot 1 Ready/Interrupt - GPIO 7 Slot 1 Card Detect - - Like the Itsy, Brutus uses a chipselect register in static memory - bank 3 for the other signals, such as voltage sense or reset: - - Read: - P0_VS1 8 Slot 0 Voltage Sense - P0_VS2 9 - P0_STSCHG 10 Slot 0 Status Change - P1_VS1 12 Slot 1 Voltage Sense - P1_VS2 13 - P1_STSCHG 14 Slot 1 Status Change - - Read/Write: - P0_ 16 Slot 0 MAX1600EAI control line - P0_ 17 Slot 0 MAX1600EAI control line - P0_ 18 Slot 0 MAX1600EAI control line - P0_ 19 Slot 0 MAX1600EAI control line - P0_ 20 Slot 0 12V - P0_ 21 Slot 0 Vpp to Vcc (CONFIRM?) - P0_ 22 Slot 0 enable fan-out drivers & xcvrs - P0_SW_RST 23 Slot 0 Reset - P1_ 24 Slot 1 MAX1600EAI control line - P1_ 25 Slot 1 MAX1600EAI control line - P1_ 26 Slot 1 MAX1600EAI control line - P1_ 27 Slot 1 MAX1600EAI control line - P1_ 28 Slot 1 12V - P1_ 29 Slot 1 Vpp to Vcc (CONFIRM?) - P1_ 30 Slot 1 enable fan-out drivers & xcvrs - P1_SW_RST 31 Slot 1 Reset - - For each slot, the bits labelled "MAX1600EAI" should (apparently) - be written with the value 0101 for Vcc 3.3V, and 1001 for Vcc 5V. - - - -Intel SA-1110 Development Platform (http://developer.intel.com/design/strong/) - - GPIO Pin Descriptions and Board Control Register - SA-1110 Microprocessor Development Board User's Guide, p.4-7, 4-10 - - The Assabet board contains only a single Compact Flash slot, - attached to slot 1 on the SA-1110. Card detect, ready, and BVD - signals are routed through GPIO, with power and reset placed in a - control register. Note that the CF bus must be enabled before use. - - GPIO 21 Slot 1 Compact Flash interrupt - GPIO 22 Slot 1 card detect (CD1 NOR CD2) - GPIO 24 Slot 1 Battery Voltage Detect 2 - GPIO 25 Slot 1 Battery Voltage Detect 1 - - Write-only: (Board Control Register - 0x12000000) - CF_PWR 0 CF bus power (3.3V) - CF_RST 1 CF reset - CF_Bus_On 7 CF bus enable - diff --git a/Documentation/arm/XScale/ADIFCC/80200EVB b/Documentation/arm/XScale/ADIFCC/80200EVB deleted file mode 100644 index 3762de418..000000000 --- a/Documentation/arm/XScale/ADIFCC/80200EVB +++ /dev/null @@ -1,110 +0,0 @@ - -Board Overview ------------------------------ - -This is an beta release of the Xscale Linux port to the ADI 80200EVB -evaluation board. - -The 80200EVB is an evaluation platform for ADI Engineering's high-performance -80200FCC chipset for the Intel 80200 XScale CPU. The 80200FCC is an open -source FPGA based system that contains a PCI unit and a high performance -memory controller. - -In addition to the 80200FCC, the board also contains a 16C550 UART, and 4MB -of flash. - -The board is still under development and currently only the UART is functional -as the PCI bits have not been programmed into the FPGA. - -For more information on the board, see http://www.adiengineering.com - -Port Status ------------------------------ - -Supported: - -- Onboard UART (Polled operation only) -- Cache/TLB locking on 80200 CPU - -TODO: - -- PCI when hardware supports it - -Building the Kernel ------------------------------ -change Linux makefile -make adi_evb_config -make oldconfig -make zImage - -Loading Linux ------------------------------ - -Before you can use Linux on the ADI board, you need to grab the following: - -ADI 80200EVB Monitor: - ftp://source.mvista.com/pub/xscale/ADI_EVB/monitor.srec - -ADI JFFS2 Image: - ftp://source.mvista.com/pub/xscale/ADI_EVB/adi.jffs2 - -Once you've got the Cygnus prompt, type in the following command: - - load - -On another terminal window: - - cat monitor.srec > /dev/ttyS0 - -(replace ttyS0 with the serial port you are using) - -Once completed, just type 'go' at the cygmon prompt and you should see: - - MontaVista IQ80310 Monitor Version 0.1 - monitor> - -Type 'b 115200' at the prompt and change your terminal speed to 115200 - -The first thing to do is to upload and burn the jffs2 filesystem image -onto the boards 4MB of flash: - - monitor> u c1000000 - Uploading file at 0xc1000000 - Now send file with ymodem - -Do as the monitor says and transfer the file adi.jffs2. Once complete, -the following will copy the jffs2 image to location 0x80000 in the flash. - - monitor> f 8000 c1000000 200000 - Erasing sector 0x00080000 - Writing sector 0x00080000 with data at 0xC1000000 - Erasing sector 0x000A0000 - Writing sector 0x000A0000 with data at 0xC1020000 - Erasing sector 0x000C0000 - ... - -Now use the same command as above to upload your zImage to location c1000000. -When you've done that, type 'j c1000000' to run Linux. Login as -root and you're all set to go. - -Misc Notes ------------------------------ - -The current version of the HW does not have an onboard timer, so the 80200 -PMU is not available for general use as it is being used for a timer source. - -By default, the MTD driver reserves the first 512K for bootloaders and -the remaining 3.5MB for the filesystem. You can edit drivers/mtd/map/adi_evb.c -to change this as needed for your application. - -Contributors ------------------------------ - -Thanks to ADI Engineering for providing the hardware for development - -Deepak Saxena - Initial port - ------------------------------ -Enjoy. If you have any problem please contact Deepak Saxena -dsaxena@mvista.com - diff --git a/Documentation/arm/XScale/IOP3XX/IQ80310 b/Documentation/arm/XScale/IOP3XX/IQ80310 deleted file mode 100644 index 5312a5742..000000000 --- a/Documentation/arm/XScale/IOP3XX/IQ80310 +++ /dev/null @@ -1,247 +0,0 @@ - -Board Overview ------------------------------ - -The Cyclone IQ80310 board is an evaluation platform for Intel's 80200 Xscale -CPU and 80312 Intelligent I/O chipset (collectively called IOP310 chipset). - -The 80312 contains dual PCI hoses (called the ATUs), a PCI-to-PCI bridge, -three DMA channels (1 on secondary PCI, one on primary PCI ), I2C, I2O -messaging unit, XOR unit for RAID operations, a bus performance monitoring -unit, and a memory controller with ECC features. - -For more information on the board, see http://developer.intel.com/iio - -Port Status ------------------------------ - -Supported: - -- MTD/JFFS/JFFS2 -- NFS root -- RAMDISK root -- 2ndary PCI slots -- Onboard ethernet -- Serial ports (ttyS0/S1) -- Cache/TLB locking on 80200 CPU -- Performance monitoring unit on 80200 CPU -- 80200 Performance Monitoring Unit -- Acting as a system controller on Cyclone 80303BP PCI backplane -- DMA engines (EXPERIMENTAL) -- 80312 Bus Performance Monitor (EXPERIMENTAL) -- Application Accelerator Unit (XOR engine for RAID) (EXPERIMENTAL) -- Messaging Unit (EXPERIMENTAL) - -TODO: -- I2C - -Building the Kernel ------------------------------ -make iq80310_config -make oldconfig -make zImage - -This will build an image setup for BOOTP/NFS root support. To change this, -just run make menuconfig and disable nfs root or add a "root=" option. - -Preparing the Hardware ------------------------------ - -This document assumes you're using a Rev D or newer board running -Redboot as the bootloader. Note that the version of RedBoot provided -with the boards has a major issue and you need to replace it with the -latest RedBoot. You can grab the source from the ECOS CVS or you can -get a prebuilt image and burn it in using FRU at: - - ftp://source.mvista.com/pub/xscale/iq80310/redboot.bin - -Make sure you do an 'fis init' command once you boot with the new -RedBoot image. - - - -Downloading Linux ------------------------------ - -Assuming you have your development system setup to act as a bootp/dhcp -server and running tftp: - - RedBoot> load -r -b 0xa1008000 /tftpboot/zImage.xs - Raw file loaded 0xa1008000-0xa1094bd8 - -If you're not using dhcp/tftp, you can use y-modem instead: - - RedBoot> load -r -b 0xa1008000 -m y - -Note that on Rev D. of the board, tftp does not work due to intermittent -interrupt issues, so you need to download using ymodem. - -Once the download is completed: - - RedBoot> go 0xa1008000 - -Root Devices ------------------------------ - -A kernel is not useful without a root filesystem, and you have several -choices with this board: NFS root, RAMDISK, or JFFS/JFFS2. For development -purposes, it is suggested that you use NFS root for easy access to various -tools. Once you're ready to deploy, probably want to utilize JFFS/JFFS2 on -the flash device. - -MTD on the IQ80310 ------------------------------ - -Linux on the IQ80310 supports RedBoot FIS paritioning if it is enabled. -Out of the box, once you've done 'fis init' on RedBoot, you will get -the following partitioning scheme: - - root@192.168.0.14:~# cat /proc/mtd - dev: size erasesize name - mtd0: 00040000 00020000 "RedBoot" - mtd1: 00040000 00020000 "RedBoot[backup]" - mtd2: 0075f000 00020000 "unallocated space" - mtd3: 00001000 00020000 "RedBoot config" - mtd4: 00020000 00020000 "FIS directory" - -To create an FIS directory, you need to use the fis command in RedBoot. -As an example, you can burn the kernel into the flash once it's downloaded: - - RedBoot> fis create -b 0xa1008000 -l 0x8CBAC -r 0xa1008000 -f 0x80000 kernel - ... Erase from 0x00080000-0x00120000: ..... - ... Program from 0xa1008000-0xa1094bac at 0x00080000: ..... - ... Unlock from 0x007e0000-0x00800000: . - ... Erase from 0x007e0000-0x00800000: . - ... Program from 0xa1fdf000-0xa1fff000 at 0x007e0000: . - ... Lock from 0x007e0000-0x00800000: . - - RedBoot> fis list - Name FLASH addr Mem addr Length Entry point - RedBoot 0x00000000 0x00000000 0x00040000 0x00000000 - RedBoot[backup] 0x00040000 0x00040000 0x00040000 0x00000000 - RedBoot config 0x007DF000 0x007DF000 0x00001000 0x00000000 - FIS directory 0x007E0000 0x007E0000 0x00020000 0x00000000 - kernel 0x00080000 0xA1008000 0x000A0000 0x00000000 - -This leads to the following Linux MTD setup: - - mtroot@192.168.0.14:~# cat /proc/mtd - dev: size erasesize name - mtd0: 00040000 00020000 "RedBoot" - mtd1: 00040000 00020000 "RedBoot[backup]" - mtd2: 000a0000 00020000 "kernel" - mtd3: 006bf000 00020000 "unallocated space" - mtd4: 00001000 00020000 "RedBoot config" - mtd5: 00020000 00020000 "FIS directory" - -Note that there is not a 1:1 mapping to the number of RedBoot paritions to -MTD partitions as unused space also gets allocated into MTD partitions. - -As an aside, the -r option when creating the Kernel entry allows you to -simply do an 'fis load kernel' to copy the image from flash into memory. -You can then do an 'fis go 0xa1008000' to start Linux. - -If you choose to use static partitioning instead of the RedBoot partioning: - - /dev/mtd0 0x00000000 - 0x0007ffff: Boot Monitor (512k) - /dev/mtd1 0x00080000 - 0x0011ffff: Kernel Image (640K) - /dev/mtd2 0x00120000 - 0x0071ffff: File System (6M) - /dev/mtd3 0x00720000 - 0x00800000: RedBoot Reserved (896K) - -To use a JFFS1/2 root FS, you need to donwload the JFFS image using either -tftp or ymodem, and then copy it to flash: - - RedBoot> load -r -b 0xa1000000 /tftpboot/jffs.img - Raw file loaded 0xa1000000-0xa1600000 - RedBoot> fis create -b 0xa1000000 -l 0x600000 -f 0x120000 jffs - ... Erase from 0x00120000-0x00720000: .................................. - ... Program from 0xa1000000-0xa1600000 at 0x00120000: .................. - ...................... - ... Unlock from 0x007e0000-0x00800000: . - ... Erase from 0x007e0000-0x00800000: . - ... Program from 0xa1fdf000-0xa1fff000 at 0x007e0000: . - ... Lock from 0x007e0000-0x00800000: . - RedBoot> fis list - Name FLASH addr Mem addr Length Entry point - RedBoot 0x00000000 0x00000000 0x00040000 0x00000000 - RedBoot[backup] 0x00040000 0x00040000 0x00040000 0x00000000 - RedBoot config 0x007DF000 0x007DF000 0x00001000 0x00000000 - FIS directory 0x007E0000 0x007E0000 0x00020000 0x00000000 - kernel 0x00080000 0xA1008000 0x000A0000 0xA1008000 - jffs 0x00120000 0x00120000 0x00600000 0x00000000 - -This looks like this in Linux: - - root@192.168.0.14:~# cat /proc/mtd - dev: size erasesize name - mtd0: 00040000 00020000 "RedBoot" - mtd1: 00040000 00020000 "RedBoot[backup]" - mtd2: 000a0000 00020000 "kernel" - mtd3: 00600000 00020000 "jffs" - mtd4: 000bf000 00020000 "unallocated space" - mtd5: 00001000 00020000 "RedBoot config" - mtd6: 00020000 00020000 "FIS directory" - -You need to boot the kernel once and watch the boot messages to see how the -JFFS RedBoot partition mapped into the MTD partition scheme. - -You can grab a pre-built JFFS image to use as a root file system at: - - ftp://source.mvista.com/pub/xscale/iq80310/jffs.img - -For detailed info on using MTD and creating a JFFS image go to: - - http://www.linux-mtd.infradead.org. - -For details on using RedBoot's FIS commands, type 'fis help' or consult -your RedBoot manual. - -Contributors ------------------------------ - -Thanks to Intel Corporation for providing the hardware. - -John Clark - Initial discovery of RedBoot issues -Dave Jiang - IRQ demux fixes, AAU, DMA, MU -Nicolas Pitre - Initial port, cleanup, debugging -Matt Porter - PCI subsystem development, debugging -Tim Sanders - Initial PCI code -Mark Salter - RedBoot fixes -Deepak Saxena - Cleanup, debug, cache lock, PMU - ------------------------------ -Enjoy. - -If you have any problems please contact Deepak Saxena - -A few notes from rmk ------------------------------ - -These are notes of my initial experience getting the IQ80310 Rev D up and -running. In total, it has taken many hours to work out what's going on... -The version of redboot used is: - - RedBoot(tm) bootstrap and debug environment, version UNKNOWN - built 14:58:21, Aug 15 2001 - - -1. I've had a corrupted download of the redboot.bin file from Montavista's - FTP site. It would be a good idea if there were md5sums, sum or gpg - signatures available to ensure the integrity of the downloaded files. - The result of this was an apparantly 100% dead card. - -2. RedBoot Intel EtherExpress Pro 100 driver seems to be very unstable - - I've had it take out the whole of a 100mbit network for several minutes. - The Hub indiates ZERO activity, despite machines attempting to communicate. - Further to this, while tftping the kernel, the transfer will stall regularly, - and might even drop the link LED. - -3. There appears to be a bug in the Intel Documentation Pack that comes with - the IQ80310 board. Serial port 1, which is the socket next to the LEDs - is address 0xfe810000, not 0xfe800000. - - Note that RedBoot uses either serial port 1 OR serial port 2, so if you - have your console connected to the wrong port, you'll see redboot messages - but not kernel boot messages. - -4. Trying to use fconfig to setup a boot script fails - it hangs when trying - to erase the flash. diff --git a/Documentation/arm/XScale/IOP3XX/IQ80321 b/Documentation/arm/XScale/IOP3XX/IQ80321 deleted file mode 100644 index e3253279d..000000000 --- a/Documentation/arm/XScale/IOP3XX/IQ80321 +++ /dev/null @@ -1,215 +0,0 @@ - -Board Overview ------------------------------ - -The Worcester IQ80321 board is an evaluation platform for Intel's 80321 Xscale -CPU (sometimes called IOP321 chipset). - -The 80321 contains a single PCI hose (called the ATUs), a PCI-to-PCI bridge, -two DMA channels, I2C, I2O messaging unit, XOR unit for RAID operations, -a bus performance monitoring unit, and a memory controller with ECC features. - -For more information on the board, see http://developer.intel.com/iio - -Port Status ------------------------------ - -Supported: - -- MTD/JFFS/JFFS2 root -- NFS root -- RAMDISK root -- Serial port (ttyS0) -- Cache/TLB locking on 80321 CPU -- Performance monitoring unit on 80321 CPU - -TODO: - -- DMA engines -- I2C -- 80321 Bus Performance Monitor -- Application Accelerator Unit (XOR engine for RAID) -- I2O Messaging Unit -- I2C unit -- SSP - -Building the Kernel ------------------------------ -make iq80321_config -make oldconfig -make zImage - -This will build an image setup for BOOTP/NFS root support. To change this, -just run make menuconfig and disable nfs root or add a "root=" option. - -Preparing the Hardware ------------------------------ - -Make sure you do an 'fis init' command once you boot with the new -RedBoot image. - -Downloading Linux ------------------------------ - -Assuming you have your development system setup to act as a bootp/dhcp -server and running tftp: - -NOTE: The 80321 board uses a different default memory map than the 80310. - - RedBoot> load -r -b 0x01008000 -m y - -Once the download is completed: - - RedBoot> go 0x01008000 - -There is a version of RedBoot floating around that has DHCP support, but -I've never been able to cleanly transfer a kernel image and have it run. - -Root Devices ------------------------------ - -A kernel is not useful without a root filesystem, and you have several -choices with this board: NFS root, RAMDISK, or JFFS/JFFS2. For development -purposes, it is suggested that you use NFS root for easy access to various -tools. Once you're ready to deploy, probably want to utilize JFFS/JFFS2 on -the flash device. - -MTD on the IQ80321 ------------------------------ - -Linux on the IQ80321 supports RedBoot FIS paritioning if it is enabled. -Out of the box, once you've done 'fis init' on RedBoot, you will get -the following partitioning scheme: - - root@192.168.0.14:~# cat /proc/mtd - dev: size erasesize name - mtd0: 00040000 00020000 "RedBoot" - mtd1: 00040000 00020000 "RedBoot[backup]" - mtd2: 0075f000 00020000 "unallocated space" - mtd3: 00001000 00020000 "RedBoot config" - mtd4: 00020000 00020000 "FIS directory" - -To create an FIS directory, you need to use the fis command in RedBoot. -As an example, you can burn the kernel into the flash once it's downloaded: - - RedBoot> fis create -b 0x01008000 -l 0x8CBAC -r 0x01008000 -f 0x80000 kernel - ... Erase from 0x00080000-0x00120000: ..... - ... Program from 0x01008000-0x01094bac at 0x00080000: ..... - ... Unlock from 0x007e0000-0x00800000: . - ... Erase from 0x007e0000-0x00800000: . - ... Program from 0x01fdf000-0x01fff000 at 0x007e0000: . - ... Lock from 0x007e0000-0x00800000: . - - RedBoot> fis list - Name FLASH addr Mem addr Length Entry point - RedBoot 0x00000000 0x00000000 0x00040000 0x00000000 - RedBoot[backup] 0x00040000 0x00040000 0x00040000 0x00000000 - RedBoot config 0x007DF000 0x007DF000 0x00001000 0x00000000 - FIS directory 0x007E0000 0x007E0000 0x00020000 0x00000000 - kernel 0x00080000 0x01008000 0x000A0000 0x00000000 - -This leads to the following Linux MTD setup: - - mtroot@192.168.0.14:~# cat /proc/mtd - dev: size erasesize name - mtd0: 00040000 00020000 "RedBoot" - mtd1: 00040000 00020000 "RedBoot[backup]" - mtd2: 000a0000 00020000 "kernel" - mtd3: 006bf000 00020000 "unallocated space" - mtd4: 00001000 00020000 "RedBoot config" - mtd5: 00020000 00020000 "FIS directory" - -Note that there is not a 1:1 mapping to the number of RedBoot paritions to -MTD partitions as unused space also gets allocated into MTD partitions. - -As an aside, the -r option when creating the Kernel entry allows you to -simply do an 'fis load kernel' to copy the image from flash into memory. -You can then do an 'fis go 0x01008000' to start Linux. - -If you choose to use static partitioning instead of the RedBoot partioning: - - /dev/mtd0 0x00000000 - 0x0007ffff: Boot Monitor (512k) - /dev/mtd1 0x00080000 - 0x0011ffff: Kernel Image (640K) - /dev/mtd2 0x00120000 - 0x0071ffff: File System (6M) - /dev/mtd3 0x00720000 - 0x00800000: RedBoot Reserved (896K) - -To use a JFFS1/2 root FS, you need to donwload the JFFS image using either -tftp or ymodem, and then copy it to flash: - - RedBoot> load -r -b 0x01000000 /tftpboot/jffs.img - Raw file loaded 0x01000000-0x01600000 - RedBoot> fis create -b 0x01000000 -l 0x600000 -f 0x120000 jffs - ... Erase from 0x00120000-0x00720000: .................................. - ... Program from 0x01000000-0x01600000 at 0x00120000: .................. - ...................... - ... Unlock from 0x007e0000-0x00800000: . - ... Erase from 0x007e0000-0x00800000: . - ... Program from 0x01fdf000-0x01fff000 at 0x007e0000: . - ... Lock from 0x007e0000-0x00800000: . - RedBoot> fis list - Name FLASH addr Mem addr Length Entry point - RedBoot 0x00000000 0x00000000 0x00040000 0x00000000 - RedBoot[backup] 0x00040000 0x00040000 0x00040000 0x00000000 - RedBoot config 0x007DF000 0x007DF000 0x00001000 0x00000000 - FIS directory 0x007E0000 0x007E0000 0x00020000 0x00000000 - kernel 0x00080000 0x01008000 0x000A0000 0x01008000 - jffs 0x00120000 0x00120000 0x00600000 0x00000000 - -This looks like this in Linux: - - root@192.168.0.14:~# cat /proc/mtd - dev: size erasesize name - mtd0: 00040000 00020000 "RedBoot" - mtd1: 00040000 00020000 "RedBoot[backup]" - mtd2: 000a0000 00020000 "kernel" - mtd3: 00600000 00020000 "jffs" - mtd4: 000bf000 00020000 "unallocated space" - mtd5: 00001000 00020000 "RedBoot config" - mtd6: 00020000 00020000 "FIS directory" - -You need to boot the kernel once and watch the boot messages to see how the -JFFS RedBoot partition mapped into the MTD partition scheme. - -You can grab a pre-built JFFS image to use as a root file system at: - - ftp://source.mvista.com/pub/xscale/iq80310/jffs.img - -For detailed info on using MTD and creating a JFFS image go to: - - http://www.linux-mtd.infradead.org. - -For details on using RedBoot's FIS commands, type 'fis help' or consult -your RedBoot manual. - -BUGS and ISSUES ------------------------------ - -* As shipped from Intel, pre-production boards have two issues: - -- The on board ethernet is disabled S8E1-2 is off. You will need to turn it on. - -- The PCIXCAPs are configured for a 100Mhz clock, but the clock selected is - actually only 66Mhz. This causes the wrong PPL multiplier to be used and the - board only runs at 400Mhz instead of 600Mhz. The way to observe this is to - use a independent clock to time a "sleep 10" command from the prompt. If it - takes 15 seconds instead of 10, you are running at 400Mhz. - -- The experimental IOP310 drivers for the AAU, DMA, etc. are not supported yet. - -Contributors ------------------------------ -The port to the IQ80321 was performed by: - -Rory Bolt - Initial port, debugging. - -This port was based on the IQ80310 port with the following contributors: - -Nicolas Pitre - Initial port, cleanup, debugging -Matt Porter - PCI subsystem development, debugging -Tim Sanders - Initial PCI code -Deepak Saxena - Cleanup, debug, cache lock, PMU - -The port is currently maintained by Deepak Saxena - ------------------------------ -Enjoy. diff --git a/Documentation/arm/XScale/IOP3XX/aau.txt b/Documentation/arm/XScale/IOP3XX/aau.txt deleted file mode 100644 index e3852ccbf..000000000 --- a/Documentation/arm/XScale/IOP3XX/aau.txt +++ /dev/null @@ -1,178 +0,0 @@ -Support functions for the Intel 80310 AAU -=========================================== - -Dave Jiang -Last updated: 09/18/2001 - -The Intel 80312 companion chip in the 80310 chipset contains an AAU. The -AAU is capable of processing up to 8 data block sources and perform XOR -operations on them. This unit is typically used to accelerated XOR -operations utilized by RAID storage device drivers such as RAID 5. This -API is designed to provide a set of functions to take adventage of the -AAU. The AAU can also be used to transfer data blocks and used as a memory -copier. The AAU transfer the memory faster than the operation performed by -using CPU copy therefore it is recommended to use the AAU for memory copy. - ------------------- -int aau_request(u32 *aau_context, const char *device_id); -This function allows the user the acquire the control of the the AAU. The -function will return a context of AAU to the user and allocate -an interrupt for the AAU. The user must pass the context as a parameter to -various AAU API calls. - -int aau_queue_buffer(u32 aau_context, aau_head_t *listhead); -This function starts the AAU operation. The user must create a SGL -header with a SGL attached. The format is presented below. The SGL is -built from kernel memory. - -/* hardware descriptor */ -typedef struct _aau_desc -{ - u32 NDA; /* next descriptor address [READONLY] */ - u32 SAR[AAU_SAR_GROUP]; /* src addrs */ - u32 DAR; /* destination addr */ - u32 BC; /* byte count */ - u32 DC; /* descriptor control */ - u32 SARE[AAU_SAR_GROUP]; /* extended src addrs */ -} aau_desc_t; - -/* user SGL format */ -typedef struct _aau_sgl -{ - aau_desc_t aau_desc; /* AAU HW Desc */ - u32 status; /* status of SGL [READONLY] */ - struct _aau_sgl *next; /* pointer to next SG [READONLY] */ - void *dest; /* destination addr */ - void *src[AAU_SAR_GROUP]; /* source addr[4] */ - void *ext_src[AAU_SAR_GROUP]; /* ext src addr[4] */ - u32 total_src; /* total number of source */ -} aau_sgl_t; - -/* header for user SGL */ -typedef struct _aau_head -{ - u32 total; /* total descriptors allocated */ - u32 status; /* SGL status */ - aau_sgl_t *list; /* ptr to head of list */ - aau_callback_t callback; /* callback func ptr */ -} aau_head_t; - - -The function will call aau_start() and start the AAU after it queues -the SGL to the processing queue. When the function will either -a. Sleep on the wait queue aau->wait_q if no callback has been provided, or -b. Continue and then call the provided callback function when DMA interrupt - has been triggered. - -int aau_suspend(u32 aau_context); -Stops/Suspends the AAU operation - -int aau_free(u32 aau_context); -Frees the ownership of AAU. Called when no longer need AAU service. - -aau_sgl_t * aau_get_buffer(u32 aau_context, int num_buf); -This function obtains an AAU SGL for the user. User must specify the number -of descriptors to be allocated in the chain that is returned. - -void aau_return_buffer(u32 aau_context, aau_sgl_t *list); -This function returns all SGL back to the API after user is done. - -int aau_memcpy(void *dest, void *src, u32 size); -This function is a short cut for user to do memory copy utilizing the AAU for -better large block memory copy vs using the CPU. This is similar to using -typical memcpy() call. - -* User is responsible for the source address(es) and the destination address. - The source and destination should all be cached memory. - - - -void aau_test() -{ - u32 aau; - char dev_id[] = "AAU"; - int size = 2; - int err = 0; - aau_head_t *head; - aau_sgl_t *list; - u32 i; - u32 result = 0; - void *src, *dest; - - printk("Starting AAU test\n"); - if((err = aau_request(&aau, dev_id))<0) - { - printk("test - AAU request failed: %d\n", err); - return; - } - else - { - printk("test - AAU request successful\n"); - } - - head = kmalloc(sizeof(aau_head_t), GFP_KERNEL); - head->total = size; - head->status = 0; - head->callback = NULL; - - list = aau_get_buffer(aau, size); - if(!list) - { - printk("Can't get buffers\n"); - return; - } - head->list = list; - - src = kmalloc(1024, GFP_KERNEL); - dest = kmalloc(1024, GFP_KERNEL); - - while(list) - { - list->status = 0; - list->aau_desc->SAR[0] = (u32)src; - list->aau_desc->DAR = (u32)dest; - list->aau_desc->BC = 1024; - - /* see iop310-aau.h for more DCR commands */ - list->aau_desc->DC = AAU_DCR_WRITE | AAU_DCR_BLKCTRL_1_DF; - if(!list->next) - { - list->aau_desc->DC = AAU_DCR_IE; - break; - } - list = list->next; - } - - printk("test- Queueing buffer for AAU operation\n"); - err = aau_queue_buffer(aau, head); - if(err >= 0) - { - printk("AAU Queue Buffer is done...\n"); - } - else - { - printk("AAU Queue Buffer failed...: %d\n", err); - } - - - -#if 1 - printk("freeing the AAU\n"); - aau_return_buffer(aau, head->list); - aau_free(aau); - kfree(src); - kfree(dest); - kfree((void *)head); -#endif -} - -All Disclaimers apply. Use this at your own discretion. Neither Intel nor I -will be responsible if anything goes wrong. =) - - -TODO -____ -* Testing -* Do zero-size AAU transfer/channel at init - so all we have to do is chainining - diff --git a/Documentation/arm/XScale/IOP3XX/dma.txt b/Documentation/arm/XScale/IOP3XX/dma.txt deleted file mode 100644 index 50c7f99e4..000000000 --- a/Documentation/arm/XScale/IOP3XX/dma.txt +++ /dev/null @@ -1,214 +0,0 @@ -Support functions forthe Intel 80310 DMA channels -================================================== - -Dave Jiang -Last updated: 09/18/2001 - -The Intel 80310 XScale chipset provides 3 DMA channels via the 80312 I/O -companion chip. Two of them resides on the primary PCI bus and one on the -secondary PCI bus. - -The DMA API provided is not compatible with the generic interface in the -ARM tree unfortunately due to how the 80312 DMACs work. Hopefully some time -in the near future a software interface can be done to bridge the differences. -The DMA API has been modeled after Nicholas Pitre's SA11x0 DMA API therefore -they will look somewhat similar. - - -80310 DMA API -------------- - -int dma_request(dmach_t channel, const char *device_id); - -This function will attempt to allocate the channel depending on what the -user requests: - -IOP310_DMA_P0: PCI Primary 1 -IOP310_DMA_P1: PCI Primary 2 -IOP310_DMA_S0: PCI Secondary 1 -/*EOF*/ - -Once the user allocates the DMA channel it is owned until released. Although -other users can also use the same DMA channel, but no new resources will be -allocated. The function will return the allocated channel number if successful. - -int dma_queue_buffer(dmach_t channel, dma_sghead_t *listhead); - -The user will construct a SGL in the form of below: -/* - * Scattered Gather DMA List for user - */ -typedef struct _dma_desc -{ - u32 NDAR; /* next descriptor adress [READONLY] */ - u32 PDAR; /* PCI address */ - u32 PUADR; /* upper PCI address */ - u32 LADR; /* local address */ - u32 BC; /* byte count */ - u32 DC; /* descriptor control */ -} dma_desc_t; - -typedef struct _dma_sgl -{ - dma_desc_t dma_desc; /* DMA descriptor */ - u32 status; /* descriptor status [READONLY] */ - u32 data; /* user defined data */ - struct _dma_sgl *next; /* next descriptor [READONLY] */ -} dma_sgl_t; - -/* dma sgl head */ -typedef struct _dma_head -{ - u32 total; /* total elements in SGL */ - u32 status; /* status of sgl */ - u32 mode; /* read or write mode */ - dma_sgl_t *list; /* pointer to list */ - dma_callback_t callback; /* callback function */ -} dma_head_t; - - -The user shall allocate user SGL elements by calling the function: -dma_get_buffer(). This function will give the user an SGL element. The user -is responsible for creating the SGL head however. The user is also -responsible for allocating the memory for DMA data. The following code segment -shows how a DMA operation can be performed: - -#include - -void dma_test(void) -{ - char dev_id[] = "Primary 0"; - dma_head_t *sgl_head = NULL; - dma_sgl_t *sgl = NULL; - int err = 0; - int channel = -1; - u32 *test_ptr = 0; - DECLARE_WAIT_QUEUE_HEAD(wait_q); - - - *(IOP310_ATUCR) = (IOP310_ATUCR_PRIM_OUT_ENAB | - IOP310_ATUCR_DIR_ADDR_ENAB); - - channel = dma_request(IOP310_DMA_P0, dev_id); - - sgl_head = (dma_head_t *)kmalloc(sizeof(dma_head_t), GFP_KERNEL); - sgl_head->callback = NULL; /* no callback created */ - sgl_head->total = 2; /* allocating 2 DMA descriptors */ - sgl_head->mode = (DMA_MOD_WRITE); - sgl_head->status = 0; - - /* now we get the two descriptors */ - sgl = dma_get_buffer(channel, 2); - - /* we set the header to point to the list we allocated */ - sgl_head->list = sgl; - - /* allocate 1k of DMA data */ - sgl->data = (u32)kmalloc(1024, GFP_KERNEL); - - /* Local address is physical */ - sgl->dma_desc.LADR = (u32)virt_to_phys(sgl->data); - - /* write to arbitrary location over the PCI bus */ - sgl->dma_desc.PDAR = 0x00600000; - sgl->dma_desc.PUADR = 0; - sgl->dma_desc.BC = 1024; - - /* set write & invalidate PCI command */ - sgl->dma_desc.DC = DMA_DCR_PCI_MWI; - sgl->status = 0; - - /* set a pattern */ - memset(sgl->data, 0xFF, 1024); - - /* User's responsibility to keep buffers cached coherent */ - cpu_dcache_clean(sgl->data, sgl->data + 1024); - - sgl = sgl->next; - - sgl->data = (u32)kmalloc(1024, GFP_KERNEL); - sgl->dma_desc.LADR = (u32)virt_to_phys(sgl->data); - sgl->dma_desc.PDAR = 0x00610000; - sgl->dma_desc.PUADR = 0; - sgl->dma_desc.BC = 1024; - - /* second descriptor has interrupt flag enabled */ - sgl->dma_desc.DC = (DMA_DCR_PCI_MWI | DMA_DCR_IE); - - /* must set end of chain flag */ - sgl->status = DMA_END_CHAIN; /* DO NOT FORGET THIS!!!! */ - - memset(sgl->data, 0x0f, 1024); - /* User's responsibility to keep buffers cached coherent */ - cpu_dcache_clean(sgl->data, sgl->data + 1024); - - /* queuing the buffer, this function will sleep since no callback */ - err = dma_queue_buffer(channel, sgl_head); - - /* now we are woken from DMA complete */ - - /* do data operations here */ - - /* free DMA data if necessary */ - - /* return the descriptors */ - dma_return_buffer(channel, sgl_head->list); - - /* free the DMA */ - dma_free(channel); - - kfree((void *)sgl_head); -} - - -dma_sgl_t * dma_get_buffer(dmach_t channel, int buf_num); - -This call allocates DMA descriptors for the user. - - -void dma_return_buffer(dmach_t channel, dma_sgl_t *list); - -This call returns the allocated descriptors back to the API. - - -int dma_suspend(dmach_t channel); - -This call suspends any DMA transfer on the given channel. - - - -int dma_resume(dmach_t channel); - -This call resumes a DMA transfer which would have been stopped through -dma_suspend(). - - -int dma_flush_all(dmach_t channel); - -This completely flushes all queued buffers and on-going DMA transfers on a -given channel. This is called when DMA channel errors have occurred. - - -void dma_free(dmach_t channel); - -This clears all activities on a given DMA channel and releases it for future -requests. - - - -Buffer Allocation ------------------ -It is the user's responsibility to allocate, free, and keep track of the -allocated DMA data memory. Upon calling dma_queue_buffer() the user must -relinquish the control of the buffers to the kernel and not change the -state of the buffers that it has passed to the kernel. The user will regain -the control of the buffers when it has been woken up by the bottom half of -the DMA interrupt handler. The user can allocate cached buffers or non-cached -via pci_alloc_consistent(). It is the user's responsibility to ensure that -the data is cache coherent. - -*Reminder* -The user is responsble to ensure the ATU is setup properly for DMA transfers. - -All Disclaimers apply. Use this at your own discretion. Neither Intel nor I -will be responsible ifanything goes wrong. diff --git a/Documentation/arm/XScale/IOP3XX/message.txt b/Documentation/arm/XScale/IOP3XX/message.txt deleted file mode 100644 index 480d13e7a..000000000 --- a/Documentation/arm/XScale/IOP3XX/message.txt +++ /dev/null @@ -1,110 +0,0 @@ -Support functions for the Intel 80310 MU -=========================================== - -Dave Jiang -Last updated: 10/11/2001 - -The messaging unit of the IOP310 contains 4 components and is utilized for -passing messages between the PCI agents on the primary bus and the Intel(R) -80200 CPU. The four components are: -Messaging Component -Doorbell Component -Circular Queues Component -Index Registers Component - -Messaging Component: -Contains 4 32bit registers, 2 in and 2 out. Writing to the registers assert -interrupt on the PCI bus or to the 80200 depend on incoming or outgoing. - -int mu_msg_request(u32 *mu_context); -Request the usage of Messaging Component. mu_context is written back by the -API. The MU context is passed to other Messaging calls as a parameter. - -int mu_msg_set_callback(u32 mu_context, u8 reg, mu_msg_cb_t func); -Setup the callback function for incoming messages. Callback can be setup for -outbound 0, 1, or both outbound registers. - -int mu_msg_post(u32 mu_context, u32 val, u8 reg); -Posting a message in the val parameter. The reg parameter denotes whether -to use register 0, 1. - -int mu_msg_free(u32 mu_context, u8 mode); -Free the usage of messaging component. mode can be specified soft or hard. In -hardmode all resources are unallocated. - -Doorbell Component: -The doorbell registers contains 1 inbound and 1 outbound. Depending on the bits -being set different interrupts are asserted. - -int mu_db_request(u32 *mu_context); -Request the usage of the doorbell register. - -int mu_db_set_callback(u32 mu_context, mu_db_cb_t func); -Setting up the inbound callback. - -void mu_db_ring(u32 mu_context, u32 mask); -Write to the outbound db register with mask. - -int mu_db_free(u32 mu_context); -Free the usage of doorbell component. - -Circular Queues Component: -The circular queue component has 4 circular queues. Inbound post, inbound free, -outbound post, outbound free. These queues are used to pass messages. - -int mu_cq_request(u32 *mu_context, u32 q_size); -Request the usage of the queue. See code comment header for q_size. It tells -the API how big of queues to setup. - -int mu_cq_inbound_init(u32 mu_context, mfa_list_t *list, u32 size, - mu_cq_cb_t func); -Init inbound queues. The user must provide a list of free message frames to -be put in inbound free queue and the callback function to handle the inbound -messages. - -int mu_cq_enable(u32 mu_context); -Enables the circular queues mechanism. Called once all the setup functions -are called. - -u32 mu_cq_get_frame(u32 mu_context); -Obtain the address of an outbound free frame for the user. - -int mu_cq_post_frame(u32 mu_context, u32 mfa); -The user can post the frame once getting the frame and put information in the -frame. - -int mu_cq_free(u32 mu_context); -Free the usage of circular queues mechanism. - -Index Registers Component: -The index register provides the mechanism to receive inbound messages. - -int mu_ir_request(u32 *mu_context); -Request of Index Register component usage. - -int mu_ir_set_callback(u32 mu_context, mu_ir_cb_t callback); -Setting up callback for inbound messages. The callback will receive the -value of the register that IAR offsets to. - -int mu_ir_free(u32 mu_context); -Free the usage of Index Registers component. - -void mu_set_irq_threshold(u32 mu_context, int thresh); -Setup the IRQ threshold before relinquish processing in IRQ space. Default -is set at 10 loops. - - -*NOTE: Example of host driver that utilize the MU can be found in the Linux I2O -driver. Specifically i2o_pci and some functions of i2o_core. The I2O driver -only utilize the circular queues mechanism. The other 3 components are simple -enough that they can be easily setup. The MU API provides no flow control for -the messaging mechanism. Flow control of the messaging needs to be established -by a higher layer of software on the IOP or the host driver. - -All Disclaimers apply. Use this at your own discretion. Neither Intel nor I -will be responsible if anything goes wrong. =) - - -TODO -____ - diff --git a/Documentation/arm/XScale/IOP3XX/pmon.txt b/Documentation/arm/XScale/IOP3XX/pmon.txt deleted file mode 100644 index 7978494a9..000000000 --- a/Documentation/arm/XScale/IOP3XX/pmon.txt +++ /dev/null @@ -1,71 +0,0 @@ - -Intel's XScale Microarchitecture 80312 companion processor provides a -Performance Monitoring Unit (PMON) that can be utilized to provide -information that can be useful for fine tuning of code. This text -file describes the API that's been developed for use by Linux kernel -programmers. Note that to get the most usage out of the PMON, -I highly reccomend getting the XScale reference manual from Intel[1] -and looking at chapter 12. - -To use the PMON, you must #include in your -source file. - -Since there's only one PMON, only one user can currently use the PMON -at a given time. To claim the PMON for usage, call iop310_pmon_claim() which -returns an identifier. When you are done using the PMON, call -iop310_pmon_release() with the id you were given earlier. - -The PMON consists of 14 registers that can be used for performance measurements. -By combining different statistics, you can derive complex performance metrics. - -To start the PMON, just call iop310_pmon_start(mode). Mode tells the PMON what -statistics to capture and can each be one of: - - IOP310_PMU_MODE0 - Performance Monitoring Disabled - - IOP310_PMU_MODE1 - Primary PCI bus and internal agents (bridge, dma Ch0, dam Ch1, patu) - - IOP310_PMU_MODE2 - Secondary PCI bus and internal agents (bridge, dma Ch0, dam Ch1, patu) - - IOP310_PMU_MODE3 - Secondary PCI bus and internal agents (external masters 0..2 and Intel - 80312 I/O companion chip) - - IOP310_PMU_MODE4 - Secondary PCI bus and internal agents (external masters 3..5 and Intel - 80312 I/O companion chip) - - IOP310_PMU_MODE5 - Intel 80312 I/O companion chip internal bus, DMA Channels and Application - Accelerator - - IOP310_PMU_MODE6 - Intel 80312 I/O companion chip internal bus, PATU, SATU and Intel 80200 - processor - - IOP310_PMU_MODE7 - Intel 80312 I/O companion chip internal bus, Primary PCI bus, Secondary - PCI bus and Secondary PCI agents (external masters 0..5 & Intel 80312 I/O - companion chip) - -To get the results back, call iop310_pmon_stop(&results) where results is -defined as follows: - -typedef struct _iop310_pmon_result -{ - u32 timestamp; /* Global Time Stamp Register */ - u32 timestamp_overflow; /* Time Stamp overflow count */ - u32 event_count[14]; /* Programmable Event Counter - Registers 1-14 */ - u32 event_overflow[14]; /* Overflow counter for PECR1-14 */ -} iop310_pmon_res_t; - - --- -This code is still under development, so please feel free to send patches, -questions, comments, etc to me. - -Deepak Saxena diff --git a/Documentation/arm/XScale/cache-lock.txt b/Documentation/arm/XScale/cache-lock.txt deleted file mode 100644 index 9728c94f1..000000000 --- a/Documentation/arm/XScale/cache-lock.txt +++ /dev/null @@ -1,123 +0,0 @@ - -Intel's XScale Microarchitecture provides support for locking of data -and instructions into the appropriate caches. This file provides -an overview of the API that has been developed to take advantage of this -feature from kernel space. Note that there is NO support for user space -cache locking. - -For example usage of this code, grab: - - ftp://source.mvista.com/pub/xscale/cache-test.c - -If you have any questions, comments, patches, etc, please contact me. - -Deepak Saxena - -API DESCRIPTION - - -I. Header File - - #include - -II. Cache Capability Discovery - - SYNOPSIS - - int cache_query(u8 cache_type, - struct cache_capabilities *pcache); - - struct cache_capabilities - { - u32 flags; /* Flags defining capabilities */ - u32 cache_size; /* Cache size in K (1024 bytes) */ - u32 max_lock; /* Maximum lockable region in K */ - } - - /* - * Flags - */ - - /* - * Bit 0: Cache lockability - * Bits 1-31: Reserved for future use - */ - #define CACHE_LOCKABLE 0x00000001 /* Cache can be locked */ - - /* - * Cache Types - */ - #define ICACHE 0x00 - #define DCACHE 0x01 - - DESCRIPTION - - This function fills out the pcache capability identifier for the - requested cache. cache_type is either DCACHE or ICACHE. This - function is not very useful at the moment as all XScale CPU's - have the same size Cache, but is is provided for future XScale - based processors that may have larger cache sizes. - - RETURN VALUE - - This function returns 0 if no error occurs, otherwise it returns - a negative, errno compatible value. - - -EIO Unknown hardware error - -III. Cache Locking - - SYNOPSIS - - int cache_lock(void *addr, u32 len, u8 cache_type, const char *desc); - - DESCRIPTION - - This function locks a physically contigous portion of memory starting - at the virtual address pointed to by addr into the cache referenced - by cache_type. - - The address of the data/instruction that is to be locked must be - aligned on a cache line boundary (L1_CACHE_ALIGNEMENT). - - The desc parameter is an optional (pass NULL if not used) human readable - descriptor of the locked memory region that is used by the cache - management code to build the /proc/cache_locks table. - - Note that this function does not check whether the address is valid - or not before locking it into the cache. That duty is up to the - caller. Also, it does not check for duplicate or overlaping - entries. - - RETURN VALUE - - If the function is successful in locking the entry into cache, a - zero is returned. - - If an error occurs, an appropriate error value is returned. - - -EINVAL The memory address provided was not cache line aligned - -ENOMEM Could not allocate memory to complete operation - -ENOSPC Not enough space left on cache to lock in requested region - -EIO Unknown error - -III. Cache Unlocking - - SYNOPSIS - - int cache_unlock(void *addr) - - DESCRIPTION - - This function unlocks a portion of memory that was previously locked - into either the I or D cache. - - RETURN VALUE - - If the entry is cleanly unlocked from the cache, a 0 is returned. - In the case of an error, an appropriate error is returned. - - -ENOENT No entry with given address associated with this cache - -EIO Unknown error - - diff --git a/Documentation/arm/XScale/pmu.txt b/Documentation/arm/XScale/pmu.txt deleted file mode 100644 index 508575d65..000000000 --- a/Documentation/arm/XScale/pmu.txt +++ /dev/null @@ -1,168 +0,0 @@ - -Intel's XScale Microarchitecture processors provide a Performance -Monitoring Unit (PMU) that can be utilized to provide information -that can be useful for fine tuning of code. This text file describes -the API that's been developed for use by Linux kernel programmers. -When I have some extra time on my hand, I will extend the code to -provide support for user mode performance monitoring (which is -probably much more useful). Note that to get the most usage out -of the PMU, I highly reccomend getting the XScale reference manual -from Intel and looking at chapter 12. - -To use the PMU, you must #include in your source file. - -Since there's only one PMU, only one user can currently use the PMU -at a given time. To claim the PMU for usage, call pmu_claim() which -returns an identifier. When you are done using the PMU, call -pmu_release() with the identifier that you were given by pmu_claim. - -In addition, the PMU can only be used on XScale based systems that -provide an external timer. Systems that the PMU is currently supported -on are: - - - Cyclone IQ80310 - -Before delving into how to use the PMU code, let's do a quick overview -of the PMU itself. The PMU consists of three registers that can be -used for performance measurements. The first is the CCNT register with -provides the number of clock cycles elapsed since the PMU was started. -The next two register, PMN0 and PMN1, are eace user programmable to -provide 1 of 20 different performance statistics. By combining different -statistics, you can derive complex performance metrics. - -To start the PMU, just call pmu_start(pm0, pmn1). pmn0 and pmn1 tell -the PMU what statistics to capture and can each be one of: - -EVT_ICACHE_MISS - Instruction fetches requiring access to external memory - -EVT_ICACHE_NO_DELIVER - Instruction cache could not deliver an instruction. Either an - ICACHE miss or an instruction TLB miss. - -EVT_ICACHE_DATA_STALL - Stall in execution due to a data dependency. This counter is - incremented each cycle in which the condition is present. - -EVT_ITLB_MISS - Instruction TLB miss - -EVT_DTLB_MISS - Data TLB miss - -EVT_BRANCH - A branch instruction was executed and it may or may not have - changed program flow - -EVT_BRANCH_MISS - A branch (B or BL instructions only) was mispredicted - -EVT_INSTRUCTION - An instruction was executed - -EVT_DCACHE_FULL_STALL - Stall because data cache buffers are full. Incremented on every - cycle in which condition is present. - -EVT_DCACHE_FULL_STALL_CONTIG - Stall because data cache buffers are full. Incremented on every - cycle in which condition is contigous. - -EVT_DCACHE_ACCESS - Data cache access (data fetch) - -EVT_DCACHE_MISS - Data cache miss - -EVT_DCACHE_WRITE_BACK - Data cache write back. This counter is incremented for every - 1/2 line (four words) that are written back. - -EVT_PC_CHANGED - Software changed the PC. This is incremented only when the - software changes the PC and there is no mode change. For example, - a MOV instruction that targets the PC would increment the counter. - An SWI would not as it triggers a mode change. - -EVT_BCU_REQUEST - The Bus Control Unit(BCU) received a request from the core - -EVT_BCU_FULL - The BCU request queue if full. A high value for this event means - that the BCU is often waiting for to complete on the external bus. - -EVT_BCU_DRAIN - The BCU queues were drained due to either a Drain Write Buffer - command or an I/O transaction for a page that was marked as - uncacheable and unbufferable. - -EVT_BCU_ECC_NO_ELOG - The BCU detected an ECC error on the memory bus but noe ELOG - register was available to to log the errors. - -EVT_BCU_1_BIT_ERR - The BCU detected a 1-bit error while reading from the bus. - -EVT_RMW - An RMW cycle occurred due to narrow write on ECC protected memory. - -To get the results back, call pmu_stop(&results) where results is defined -as a struct pmu_results: - - struct pmu_results - { - u32 ccnt; /* Clock Counter Register */ - u32 ccnt_of; / - u32 pmn0; /* Performance Counter Register 0 */ - u32 pmn0_of; - u32 pmn1; /* Performance Counter Register 1 */ - u32 pmn1_of; - }; - -Pretty simple huh? Following are some examples of how to get some commonly -wanted numbers out of the PMU data. Note that since you will be dividing -things, this isn't super useful from the kernel and you need to printk the -data out to syslog. See [1] for more examples. - -Instruction Cache Efficiency - - pmu_start(EVT_INSTRUCTION, EVT_ICACHE_MISS); - ... - pmu_stop(&results); - - icache_miss_rage = results.pmn1 / results.pmn0; - cycles_per_instruction = results.ccnt / results.pmn0; - -Data Cache Efficiency - - pmu_start(EVT_DCACHE_ACCESS, EVT_DCACHE_MISS); - ... - pmu_stop(&results); - - dcache_miss_rage = results.pmn1 / results.pmn0; - -Instruction Fetch Latency - - pmu_start(EVT_ICACHE_NO_DELIVER, EVT_ICACHE_MISS); - ... - pmu_stop(&results); - - average_stall_waiting_for_instruction_fetch = - results.pmn0 / results.pmn1; - - percent_stall_cycles_due_to_instruction_fetch = - results.pmn0 / results.ccnt; - - -ToDo: - -- Add support for usermode PMU usage. This might require hooking into - the scheduler so that we pause the PMU when the task that requested - statistics is scheduled out. - --- -This code is still under development, so please feel free to send patches, -questions, comments, etc to me. - -Deepak Saxena - diff --git a/Documentation/arm/XScale/tlb-lock.txt b/Documentation/arm/XScale/tlb-lock.txt deleted file mode 100644 index 1ba3e11d0..000000000 --- a/Documentation/arm/XScale/tlb-lock.txt +++ /dev/null @@ -1,64 +0,0 @@ - -Intel's XScale Microarchitecture provides support for locking of TLB -entries in both the instruction and data TLBs. This file provides -an overview of the API that has been developed to take advantage of this -feature from kernel space. Note that there is NO support for user space. - -In general, this feature should be used in conjunction with locking -data or instructions into the appropriate caches. See the file -cache-lock.txt in this directory. - -If you have any questions, comments, patches, etc, please contact me. - -Deepak Saxena - - -API DESCRIPTION - -I. Header file - - #include - -II. Locking an entry into the TLB - - SYNOPSIS - - xscale_tlb_lock(u8 tlb_type, u32 addr); - - /* - * TLB types - */ - #define ITLB 0x0 - #define DTLB 0x1 - - DESCRIPTION - - This function locks the virtual to physical mapping for virtual - address addr into the requested TLB. - - RETURN VALUE - - If the entry is properly locked into the TLB, a 0 is returned. - In case of an error, an appropriate error is returned. - - -ENOSPC No more entries left in the TLB - -EIO Unknown error - -III. Unlocking an entry from a TLB - - SYNOPSIS - - xscale_tlb_unlock(u8 tlb_type, u32 addr); - - DESCRIPTION - - This function unlocks the entry for virtual address addr from the - specified cache. - - RETURN VALUE - - If the TLB entry is properly unlocked, a 0 is returned. - In case of an error, an appropriate error is returned. - - -ENOENT No entry for given address in specified TLB - diff --git a/Documentation/cpu-freq/amd-powernow.txt b/Documentation/cpu-freq/amd-powernow.txt new file mode 100644 index 000000000..254da155f --- /dev/null +++ b/Documentation/cpu-freq/amd-powernow.txt @@ -0,0 +1,38 @@ + +PowerNow! and Cool'n'Quiet are AMD names for frequency +management capabilities in AMD processors. As the hardware +implementation changes in new generations of the processors, +there is a different cpu-freq driver for each generation. + +Note that the driver's will not load on the "wrong" hardware, +so it is safe to try each driver in turn when in doubt as to +which is the correct driver. + +Note that the functionality to change frequency (and voltage) +is not available in all processors. The drivers will refuse +to load on processors without this capability. The capability +is detected with the cpuid instruction. + +The drivers use BIOS supplied tables to obtain frequency and +voltage information appropriate for a particular platform. +Frequency transitions will be unavailable if the BIOS does +not supply these tables. + +6th Generation: powernow-k6 + +7th Generation: powernow-k7: Athlon, Duron, Geode. + +8th Generation: powernow-k8: Athlon, Athlon 64, Opteron, Sempron. +Documentation on this functionality in 8th generation processors +is available in the "BIOS and Kernel Developer's Guide", publication +26094, in chapter 9, available for download from www.amd.com. + +BIOS supplied data, for powernow-k7 and for powernow-k8, may be +from either the PSB table or from ACPI objects. The ACPI support +is only available if the kernel config sets CONFIG_ACPI_PROCESSOR. +The powernow-k8 driver will attempt to use ACPI if so configured, +and fall back to PST if that fails. +The powernow-k7 driver will try to use the PSB support first, and +fall back to ACPI if the PSB support fails. A module parameter, +acpi_force, is provided to force ACPI support to be used instead +of PSB support. diff --git a/Documentation/filesystems/relayfs.txt b/Documentation/filesystems/relayfs.txt deleted file mode 100644 index 7397bdb23..000000000 --- a/Documentation/filesystems/relayfs.txt +++ /dev/null @@ -1,812 +0,0 @@ - -relayfs - a high-speed data relay filesystem -============================================ - -relayfs is a filesystem designed to provide an efficient mechanism for -tools and facilities to relay large amounts of data from kernel space -to user space. - -The main idea behind relayfs is that every data flow is put into a -separate "channel" and each channel is a file. In practice, each -channel is a separate memory buffer allocated from within kernel space -upon channel instantiation. Software needing to relay data to user -space would open a channel or a number of channels, depending on its -needs, and would log data to that channel. All the buffering and -locking mechanics are taken care of by relayfs. The actual format and -protocol used for each channel is up to relayfs' clients. - -relayfs makes no provisions for copying the same data to more than a -single channel. This is for the clients of the relay to take care of, -and so is any form of data filtering. The purpose is to keep relayfs -as simple as possible. - - -Usage -===== - -In addition to the relayfs kernel API described below, relayfs -implements basic file operations. Here are the file operations that -are available and some comments regarding their behavior: - -open() enables user to open an _existing_ channel. A channel can be - opened in blocking or non-blocking mode, and can be opened - for reading as well as for writing. Readers will by default - be auto-consuming. - -mmap() results in channel's memory buffer being mmapped into the - caller's memory space. - -read() since we are dealing with circular buffers, the user is only - allowed to read forward. Some apps may want to loop around - read() waiting for incoming data - if there is no data - available, read will put the reader on a wait queue until - data is available (blocking mode). Non-blocking reads return - -EAGAIN if data is not available. - - -write() writing from user space operates exactly as relay_write() does - (described below). - -poll() POLLIN/POLLRDNORM/POLLOUT/POLLWRNORM/POLLERR supported. - -close() decrements the channel's refcount. When the refcount reaches - 0 i.e. when no process or kernel client has the file open - (see relay_close() below), the channel buffer is freed. - - -In order for a user application to make use of relayfs files, the -relayfs filesystem must be mounted. For example, - - mount -t relayfs relayfs /mountpoint - - -The relayfs kernel API -====================== - -relayfs channels are implemented as circular buffers subdivided into -'sub-buffers'. kernel clients write data into the channel using -relay_write(), and are notified via a set of callbacks when -significant events occur within the channel. 'Significant events' -include: - -- a sub-buffer has been filled i.e. the current write won't fit into the - current sub-buffer, and a 'buffer-switch' is triggered, after which - the data is written into the next buffer (if the next buffer is - empty). The client is notified of this condition via two callbacks, - one providing an opportunity to perform start-of-buffer tasks, the - other end-of-buffer tasks. - -- data is ready for the client to process. The client can choose to - be notified either on a per-sub-buffer basis (bulk delivery) or - per-write basis (packet delivery). - -- data has been written to the channel from user space. The client can - use this notification to accept and process 'commands' sent to the - channel via write(2). - -- the channel has been opened/closed/mapped/unmapped from user space. - The client can use this notification to trigger actions within the - kernel application, such as enabling/disabling logging to the - channel. It can also return result codes from the callback, - indicating that the operation should fail e.g. in order to restrict - more than one user space open or mmap. - -- the channel needs resizing, or needs to update its - state based on the results of the resize. Resizing the channel is - up to the kernel client to actually perform. If the channel is - configured for resizing, the client is notified when the unread data - in the channel passes a preset threshold, giving it the opportunity - to allocate a new channel buffer and replace the old one. - -Reader objects --------------- - -Channel readers use an opaque rchan_reader object to read from -channels. For VFS readers (those using read(2) to read from a -channel), these objects are automatically created and used internally; -only kernel clients that need to directly read from channels, or whose -userspace applications use mmap to access channel data, need to know -anything about rchan_readers - others may skip this section. - -A relay channel can have any number of readers, each represented by an -rchan_reader instance, which is used to encapsulate reader settings -and state. rchan_reader objects should be treated as opaque by kernel -clients. To create a reader object for directly accessing a channel -from kernel space, call the add_rchan_reader() kernel API function: - -rchan_reader *add_rchan_reader(rchan_id, auto_consume) - -This function returns an rchan_reader instance if successful, which -should then be passed to relay_read() when the kernel client is -interested in reading from the channel. - -The auto_consume parameter indicates whether a read done by this -reader will automatically 'consume' that portion of the unread channel -buffer when relay_read() is called (see below for more details). - -To close the reader, call - -remove_rchan_reader(reader) - -which will remove the reader from the list of current readers. - - -To create a reader object representing a userspace mmap reader in the -kernel application, call the add_map_reader() kernel API function: - -rchan_reader *add_map_reader(rchan_id) - -This function returns an rchan_reader instance if successful, whose -main purpose is as an argument to be passed into -relay_buffers_consumed() when the kernel client becomes aware that -data has been read by a user application using mmap to read from the -channel buffer. There is no auto_consume option in this case, since -only the kernel client/user application knows when data has been read. - -To close the map reader, call - -remove_map_reader(reader) - -which will remove the reader from the list of current readers. - -Consumed count --------------- - -A relayfs channel is a circular buffer, which means that if there is -no reader reading from it or a reader reading too slowly, at some -point the channel writer will 'lap' the reader and data will be lost. -In normal use, readers will always be able to keep up with writers and -the buffer is thus never in danger of becoming full. In many -applications, it's sufficient to ensure that this is practically -speaking always the case, by making the buffers large enough. These -types of applications can basically open the channel as -RELAY_MODE_CONTINOUS (the default anyway) and not worry about the -meaning of 'consume' and skip the rest of this section. - -If it's important for the application that a kernel client never allow -writers to overwrite unread data, the channel should be opened using -RELAY_MODE_NO_OVERWRITE and must be kept apprised of the count of -bytes actually read by the (typically) user-space channel readers. -This count is referred to as the 'consumed count'. read(2) channel -readers automatically update the channel's 'consumed count' as they -read. If the usage mode is to have only read(2) readers, which is -typically the case, the kernel client doesn't need to worry about any -of the relayfs functions having to do with 'bytes consumed' and can -skip the rest of this section. (Note that it is possible to have -multiple read(2) or auto-consuming readers, but like having multiple -readers on a pipe, these readers will race with each other i.e. it's -supported, but doesn't make much sense). - -If the kernel client cannot rely on an auto-consuming reader to keep -the 'consumed count' up-to-date, then it must do so manually, by -making the appropriate calls to relay_buffers_consumed() or -relay_bytes_consumed(). In most cases, this should only be necessary -for bulk mmap clients - almost all packet clients should be covered by -having auto-consuming read(2) readers. For mmapped bulk clients, for -instance, there are no auto-consuming VFS readers, so the kernel -client needs to make the call to relay_buffers_consumed() after -sub-buffers are read. - -Kernel API ----------- - -Here's a summary of the API relayfs provides to in-kernel clients: - -int relay_open(channel_path, bufsize, nbufs, channel_flags, - channel_callbacks, start_reserve, end_reserve, - rchan_start_reserve, resize_min, resize_max, mode, - init_buf, init_buf_size) -int relay_write(channel_id, *data_ptr, count, time_delta_offset, **wrote) -rchan_reader *add_rchan_reader(channel_id, auto_consume) -int remove_rchan_reader(rchan_reader *reader) -rchan_reader *add_map_reader(channel_id) -int remove_map_reader(rchan_reader *reader) -int relay_read(reader, buf, count, wait, *actual_read_offset) -void relay_buffers_consumed(reader, buffers_consumed) -void relay_bytes_consumed(reader, bytes_consumed, read_offset) -int relay_bytes_avail(reader) -int rchan_full(reader) -int rchan_empty(reader) -int relay_info(channel_id, *channel_info) -int relay_close(channel_id) -int relay_realloc_buffer(channel_id, nbufs, async) -int relay_replace_buffer(channel_id) -int relay_reset(int rchan_id) - ----------- -int relay_open(channel_path, bufsize, nbufs, - channel_flags, channel_callbacks, start_reserve, - end_reserve, rchan_start_reserve, resize_min, resize_max, mode) - -relay_open() is used to create a new entry in relayfs. This new entry -is created according to channel_path. channel_path contains the -absolute path to the channel file on relayfs. If, for example, the -caller sets channel_path to "/xlog/9", a "xlog/9" entry will appear -within relayfs automatically and the "xlog" directory will be created -in the filesystem's root. relayfs does not implement any policy on -its content, except to disallow the opening of two channels using the -same file. There are, nevertheless a set of guidelines for using -relayfs. Basically, each facility using relayfs should use a top-level -directory identifying it. The entry created above, for example, -presumably belongs to the "xlog" software. - -The remaining parameters for relay_open() are as follows: - -- channel_flags - an ORed combination of attribute values controlling - common channel characteristics: - - - logging scheme - relayfs use 2 mutually exclusive schemes - for logging data to a channel. The 'lockless scheme' - reserves and writes data to a channel without the need of - any type of locking on the channel. This is the preferred - scheme, but may not be available on a given architecture (it - relies on the presence of a cmpxchg instruction). It's - specified by the RELAY_SCHEME_LOCKLESS flag. The 'locking - scheme' either obtains a lock on the channel for writing or - disables interrupts, depending on whether the channel was - opened for SMP or global usage (see below). It's specified - by the RELAY_SCHEME_LOCKING flag. While a client may want - to explicitly specify a particular scheme to use, it's more - convenient to specify RELAY_SCHEME_ANY for this flag, which - will allow relayfs to choose the best available scheme i.e. - lockless if supported. - - - overwrite mode (default is RELAY_MODE_CONTINUOUS) - - If RELAY_MODE_CONTINUOUS is specified, writes to the channel - will succeed regardless of whether there are up-to-date - consumers or not. If RELAY_MODE_NO_OVERWRITE is specified, - the channel becomes 'full' when the total amount of buffer - space unconsumed by readers equals or exceeds the total - buffer size. With the buffer in this state, writes to the - buffer will fail - clients need to check the return code from - relay_write() to determine if this is the case and act - accordingly - 0 or a negative value indicate the write failed. - - - SMP usage - this applies only when the locking scheme is in - use. If RELAY_USAGE_SMP is specified, it's assumed that the - channel will be used in a per-CPU fashion and consequently, - the only locking that will be done for writes is to disable - local irqs. If RELAY_USAGE_GLOBAL is specified, it's assumed - that writes to the buffer can occur within any CPU context, - and spinlock_irq_save will be used to lock the buffer. - - - delivery mode - if RELAY_DELIVERY_BULK is specified, the - client will be notified via its deliver() callback whenever a - sub-buffer has been filled. Alternatively, - RELAY_DELIVERY_PACKET will cause delivery to occur after the - completion of each write. See the description of the channel - callbacks below for more details. - - - timestamping - if RELAY_TIMESTAMP_TSC is specified and the - architecture supports it, efficient TSC 'timestamps' can be - associated with each write, otherwise more expensive - gettimeofday() timestamping is used. At the beginning of - each sub-buffer, a gettimeofday() timestamp and the current - TSC, if supported, are read, and are passed on to the client - via the buffer_start() callback. This allows correlation of - the current time with the current TSC for subsequent writes. - Each subsequent write is associated with a 'time delta', - which is either the current TSC, if the channel is using - TSCs, or the difference between the buffer_start gettimeofday - timestamp and the gettimeofday time read for the current - write. Note that relayfs never writes either a timestamp or - time delta into the buffer unless explicitly asked to (see - the description of relay_write() for details). - -- bufsize - the size of the 'sub-buffers' making up the circular channel - buffer. For the lockless scheme, this must be a power of 2. - -- nbufs - the number of 'sub-buffers' making up the circular - channel buffer. This must be a power of 2. - - The total size of the channel buffer is bufsize * nbufs rounded up - to the next kernel page size. If the lockless scheme is used, both - bufsize and nbufs must be a power of 2. If the locking scheme is - used, the bufsize can be anything and nbufs must be a power of 2. If - RELAY_SCHEME_ANY is used, the bufsize and nbufs should be a power of 2. - - NOTE: if nbufs is 1, relayfs will bypass the normal size - checks and will allocate an rvmalloced buffer of size bufsize. - This buffer will be freed when relay_close() is called, if the channel - isn't still being referenced. - -- callbacks - a table of callback functions called when events occur - within the data relay that clients need to know about: - - - int buffer_start(channel_id, current_write_pos, buffer_id, - start_time, start_tsc, using_tsc) - - - called at the beginning of a new sub-buffer, the - buffer_start() callback gives the client an opportunity to - write data into space reserved at the beginning of a - sub-buffer. The client should only write into the buffer - if it specified a value for start_reserve and/or - channel_start_reserve (see below) when the channel was - opened. In the latter case, the client can determine - whether to write its one-time rchan_start_reserve data by - examining the value of buffer_id, which will be 0 for the - first sub-buffer. The address that the client can write - to is contained in current_write_pos (the client by - definition knows how much it can write i.e. the value it - passed to relay_open() for start_reserve/ - channel_start_reserve). start_time contains the - gettimeofday() value for the start of the buffer and start - TSC contains the TSC read at the same time. The using_tsc - param indicates whether or not start_tsc is valid (it - wouldn't be if TSC timestamping isn't being used). - - The client should return the number of bytes it wrote to - the channel, 0 if none. - - - int buffer_end(channel_id, current_write_pos, end_of_buffer, - end_time, end_tsc, using_tsc) - - called at the end of a sub-buffer, the buffer_end() - callback gives the client an opportunity to perform - end-of-buffer processing. Note that the current_write_pos - is the position where the next write would occur, but - since the current write wouldn't fit (which is the trigger - for the buffer_end event), the buffer is considered full - even though there may be unused space at the end. The - end_of_buffer param pointer value can be used to determine - exactly the size of the unused space. The client should - only write into the buffer if it specified a value for - end_reserve when the channel was opened. If the client - doesn't write anything i.e. returns 0, the unused space at - the end of the sub-buffer is available via relay_info() - - this data may be needed by the client later if it needs to - process raw sub-buffers (an alternative would be to save - the unused bytes count value in end_reserve space at the - end of each sub-buffer during buffer_end processing and - read it when needed at a later time. The other - alternative would be to use read(2), which makes the - unused count invisible to the caller). end_time contains - the gettimeofday() value for the end of the buffer and end - TSC contains the TSC read at the same time. The using_tsc - param indicates whether or not end_tsc is valid (it - wouldn't be if TSC timestamping isn't being used). - - The client should return the number of bytes it wrote to - the channel, 0 if none. - - - void deliver(channel_id, from, len) - - called when data is ready for the client. This callback - is used to notify a client when a sub-buffer is complete - (in the case of bulk delivery) or a single write is - complete (packet delivery). A bulk delivery client might - wish to then signal a daemon that a sub-buffer is ready. - A packet delivery client might wish to process the packet - or send it elsewhere. The from param is a pointer to the - delivered data and len specifies how many bytes are ready. - - - void user_deliver(channel_id, from, len) - - called when data has been written to the channel from user - space. This callback is used to notify a client when a - successful write from userspace has occurred, independent - of whether bulk or packet delivery is in use. This can be - used to allow userspace programs to communicate with the - kernel client through the channel via out-of-band write(2) - 'commands' instead of via ioctls, for instance. The from - param is a pointer to the delivered data and len specifies - how many bytes are ready. Note that this callback occurs - after the bytes have been successfully written into the - channel, which means that channel readers must be able to - deal with the 'command' data which will appear in the - channel data stream just as any other userspace or - non-userspace write would. - - - int needs_resize(channel_id, resize_type, - suggested_buf_size, suggested_n_bufs) - - called when a channel's buffers are in danger of becoming - full i.e. the number of unread bytes in the channel passes - a preset threshold, or when the current capacity of a - channel's buffer is no longer needed. Also called to - notify the client when a channel's buffer has been - replaced. If resize_type is RELAY_RESIZE_EXPAND or - RELAY_RESIZE_SHRINK, the kernel client should arrange to - call relay_realloc_buffer() with the suggested buffer size - and buffer count, which will allocate (but will not - replace the old one) a new buffer of the recommended size - for the channel. When the allocation has completed, - needs_resize() is again called, this time with a - resize_type of RELAY_RESIZE_REPLACE. The kernel client - should then arrange to call relay_replace_buffer() to - actually replace the old channel buffer with the newly - allocated buffer. Finally, once the buffer replacement - has completed, needs_resize() is again called, this time - with a resize_type of RELAY_RESIZE_REPLACED, to inform the - client that the replacement is complete and additionally - confirming the current sub-buffer size and number of - sub-buffers. Note that a resize can be canceled if - relay_realloc_buffer() is called with the async param - non-zero and the resize conditions no longer hold. In - this case, the RELAY_RESIZE_REPLACED suggested number of - sub-buffers will be the same as the number of sub-buffers - that existed before the RELAY_RESIZE_SHRINK or EXPAND i.e. - values indicating that the resize didn't actually occur. - - - int fileop_notify(channel_id, struct file *filp, enum relay_fileop) - - called when a userspace file operation has occurred or - will occur on a relayfs channel file. These notifications - can be used by the kernel client to trigger actions within - the kernel client when the corresponding event occurs, - such as enabling logging only when a userspace application - opens or mmaps a relayfs file and disabling it again when - the file is closed or unmapped. The kernel client can - also return its own return value, which can affect the - outcome of file operation - returning 0 indicates that the - operation should succeed, and returning a negative value - indicates that the operation should be failed, and that - the returned value should be returned to the ultimate - caller e.g. returning -EPERM from the open fileop will - cause the open to fail with -EPERM. Among other things, - the return value can be used to restrict a relayfs file - from being opened or mmap'ed more than once. The currently - implemented fileops are: - - RELAY_FILE_OPEN - a relayfs file is being opened. Return - 0 to allow it to succeed, negative to - have it fail. A negative return value will - be passed on unmodified to the open fileop. - RELAY_FILE_CLOSE- a relayfs file is being closed. The return - value is ignored. - RELAY_FILE_MAP - a relayfs file is being mmap'ed. Return 0 - to allow it to succeed, negative to have - it fail. A negative return value will be - passed on unmodified to the mmap fileop. - RELAY_FILE_UNMAP- a relayfs file is being unmapped. The return - value is ignored. - - - void ioctl(rchan_id, cmd, arg) - - called when an ioctl call is made using a relayfs file - descriptor. The cmd and arg are passed along to this - callback unmodified for it to do as it wishes with. The - return value from this callback is used as the return value - of the ioctl call. - - If the callbacks param passed to relay_open() is NULL, a set of - default do-nothing callbacks will be defined for the channel. - Likewise, any NULL rchan_callback function contained in a non-NULL - callbacks struct will be filled in with a default callback function - that does nothing. - -- start_reserve - the number of bytes to be reserved at the start of - each sub-buffer. The client can do what it wants with this number - of bytes when the buffer_start() callback is invoked. Typically - clients would use this to write per-sub-buffer header data. - -- end_reserve - the number of bytes to be reserved at the end of each - sub-buffer. The client can do what it wants with this number of - bytes when the buffer_end() callback is invoked. Typically clients - would use this to write per-sub-buffer footer data. - -- channel_start_reserve - the number of bytes to be reserved, in - addition to start_reserve, at the beginning of the first sub-buffer - in the channel. The client can do what it wants with this number of - bytes when the buffer_start() callback is invoked. Typically - clients would use this to write per-channel header data. - -- resize_min - if set, this signifies that the channel is - auto-resizeable. The value specifies the size that the channel will - try to maintain as a normal working size, and that it won't go - below. The client makes use of the resizing callbacks and - relay_realloc_buffer() and relay_replace_buffer() to actually effect - the resize. - -- resize_max - if set, this signifies that the channel is - auto-resizeable. The value specifies the maximum size the channel - can have as a result of resizing. - -- mode - if non-zero, specifies the file permissions that will be given - to the channel file. If 0, the default rw user perms will be used. - -- init_buf - if non-NULL, rather than allocating the channel buffer, - this buffer will be used as the initial channel buffer. The kernel - API function relay_discard_init_buf() can later be used to have - relayfs allocate a normal mmappable channel buffer and switch over - to using it after copying the init_buf contents into it. Currently, - the size of init_buf must be exactly buf_size * n_bufs. The caller - is responsible for managing the init_buf memory. This feature is - typically used for init-time channel use and should normally be - specified as NULL. - -- init_buf_size - the total size of init_buf, if init_buf is specified - as non-NULL. Currently, the size of init_buf must be exactly - buf_size * n_bufs. - -Upon successful completion, relay_open() returns a channel id -to be used for all other operations with the relay. All buffers -managed by the relay are allocated using rvmalloc/rvfree to allow -for easy mmapping to user-space. - ----------- -int relay_write(channel_id, *data_ptr, count, time_delta_offset, **wrote_pos) - -relay_write() reserves space in the channel and writes count bytes of -data pointed to by data_ptr to it. Automatically performs any -necessary locking, depending on the scheme and SMP usage in effect (no -locking is done for the lockless scheme regardless of usage). It -returns the number of bytes written, or 0/negative on failure. If -time_delta_offset is >= 0, the internal time delta, the internal time -delta calculated when the slot was reserved will be written at that -offset. This is the TSC or gettimeofday() delta between the current -write and the beginning of the buffer, whichever method is being used -by the channel. Trying to write a count larger than the bufsize -specified to relay_open() (taking into account the reserved -start-of-buffer and end-of-buffer space as well) will fail. If -wrote_pos is non-NULL, it will receive the location the data was -written to, which may be needed for some applications but is not -normally interesting. Most applications should pass in NULL for this -param. - ----------- -struct rchan_reader *add_rchan_reader(int rchan_id, int auto_consume) - -add_rchan_reader creates and initializes a reader object for a -channel. An opaque rchan_reader object is returned on success, and is -passed to relay_read() when reading the channel. If the boolean -auto_consume parameter is 1, the reader is defined to be -auto-consuming. auto-consuming reader objects are automatically -created and used for VFS read(2) readers. - ----------- -void remove_rchan_reader(struct rchan_reader *reader) - -remove_rchan_reader finds and removes the given reader from the -channel. This function is used only by non-VFS read(2) readers. VFS -read(2) readers are automatically removed when the corresponding file -object is closed. - ----------- -reader add_map_reader(int rchan_id) - -Creates and initializes an rchan_reader object for channel map -readers, and is needed for updating relay_bytes/buffers_consumed() -when kernel clients become aware of the need to do so by their mmap -user clients. - ----------- -int remove_map_reader(reader) - -Finds and removes the given map reader from the channel. This function -is useful only for map readers. - ----------- -int relay_read(reader, buf, count, wait, *actual_read_offset) - -Reads count bytes from the channel, or as much as is available within -the sub-buffer currently being read. The read offset that will be -read from is the position contained within the reader object. If the -wait flag is set, buf is non-NULL, and there is nothing available, it -will wait until there is. If the wait flag is 0 and there is nothing -available, -EAGAIN is returned. If buf is NULL, the value returned is -the number of bytes that would have been read. actual_read_offset is -the value that should be passed as the read offset to -relay_bytes_consumed, needed only if the reader is not auto-consuming -and the channel is MODE_NO_OVERWRITE, but in any case, it must not be -NULL. - ----------- - -int relay_bytes_avail(reader) - -Returns the number of bytes available relative to the reader's current -read position within the corresponding sub-buffer, 0 if there is -nothing available. Note that this doesn't return the total bytes -available in the channel buffer - this is enough though to know if -anything is available, however, or how many bytes might be returned -from the next read. - ----------- -void relay_buffers_consumed(reader, buffers_consumed) - -Adds to the channel's consumed buffer count. buffers_consumed should -be the number of buffers newly consumed, not the total number -consumed. NOTE: kernel clients don't need to call this function if -the reader is auto-consuming or the channel is MODE_CONTINUOUS. - -In order for the relay to detect the 'buffers full' condition for a -channel, it must be kept up-to-date with respect to the number of -buffers consumed by the client. If the addition of the value of the -bufs_consumed param to the current bufs_consumed count for the channel -would exceed the bufs_produced count for the channel, the channel's -bufs_consumed count will be set to the bufs_produced count for the -channel. This allows clients to 'catch up' if necessary. - ----------- -void relay_bytes_consumed(reader, bytes_consumed, read_offset) - -Adds to the channel's consumed count. bytes_consumed should be the -number of bytes actually read e.g. return value of relay_read() and -the read_offset should be the actual offset the bytes were read from -e.g. the actual_read_offset set by relay_read(). NOTE: kernel clients -don't need to call this function if the reader is auto-consuming or -the channel is MODE_CONTINUOUS. - -In order for the relay to detect the 'buffers full' condition for a -channel, it must be kept up-to-date with respect to the number of -bytes consumed by the client. For packet clients, it makes more sense -to update after each read rather than after each complete sub-buffer -read. The bytes_consumed count updates bufs_consumed when a buffer -has been consumed so this count remains consistent. - ----------- -int relay_info(channel_id, *channel_info) - -relay_info() fills in an rchan_info struct with channel status and -attribute information such as usage modes, sub-buffer size and count, -the allocated size of the entire buffer, buffers produced and -consumed, current buffer id, count of writes lost due to buffers full -condition. - -The virtual address of the channel buffer is also available here, for -those clients that need it. - -Clients may need to know how many 'unused' bytes there are at the end -of a given sub-buffer. This would only be the case if the client 1) -didn't either write this count to the end of the sub-buffer or -otherwise note it (it's available as the difference between the buffer -end and current write pos params in the buffer_end callback) (if the -client returned 0 from the buffer_end callback, it's assumed that this -is indeed the case) 2) isn't using the read() system call to read the -buffer. In other words, if the client isn't annotating the stream and -is reading the buffer by mmaping it, this information would be needed -in order for the client to 'skip over' the unused bytes at the ends of -sub-buffers. - -Additionally, for the lockless scheme, clients may need to know -whether a particular sub-buffer is actually complete. An array of -boolean values, one per sub-buffer, contains non-zero if the buffer is -complete, non-zero otherwise. - ----------- -int relay_close(channel_id) - -relay_close() is used to close the channel. It finalizes the last -sub-buffer (the one currently being written to) and marks the channel -as finalized. The channel buffer and channel data structure are then -freed automatically when the last reference to the channel is given -up. - ----------- -int relay_realloc_buffer(channel_id, nbufs, async) - -Allocates a new channel buffer using the specified sub-buffer count -(note that resizing can't change sub-buffer sizes). If async is -non-zero, the allocation is done in the background using a work queue. -When the allocation has completed, the needs_resize() callback is -called with a resize_type of RELAY_RESIZE_REPLACE. This function -doesn't replace the old buffer with the new - see -relay_replace_buffer(). - -This function is called by kernel clients in response to a -needs_resize() callback call with a resize type of RELAY_RESIZE_EXPAND -or RELAY_RESIZE_SHRINK. That callback also includes a suggested -new_bufsize and new_nbufs which should be used when calling this -function. - -Returns 0 on success, or errcode if the channel is busy or if -the allocation couldn't happen for some reason. - -NOTE: if async is not set, this function should not be called with a -lock held, as it may sleep. - ----------- -int relay_replace_buffer(channel_id) - -Replaces the current channel buffer with the new buffer allocated by -relay_realloc_buffer and contained in the channel struct. When the -replacement is complete, the needs_resize() callback is called with -RELAY_RESIZE_REPLACED. This function is called by kernel clients in -response to a needs_resize() callback having a resize type of -RELAY_RESIZE_REPLACE. - -Returns 0 on success, or errcode if the channel is busy or if the -replacement or previous allocation didn't happen for some reason. - -NOTE: This function will not sleep, so can called in any context and -with locks held. The client should, however, ensure that the channel -isn't actively being read from or written to. - ----------- -int relay_reset(rchan_id) - -relay_reset() has the effect of erasing all data from the buffer and -restarting the channel in its initial state. The buffer itself is not -freed, so any mappings are still in effect. NOTE: Care should be -taken that the channnel isn't actually being used by anything when -this call is made. - ----------- -int rchan_full(reader) - -returns 1 if the channel is full with respect to the reader, 0 if not. - ----------- -int rchan_empty(reader) - -returns 1 if the channel is empty with respect to the reader, 0 if not. - ----------- -int relay_discard_init_buf(rchan_id) - -allocates an mmappable channel buffer, copies the contents of init_buf -into it, and sets the current channel buffer to the newly allocated -buffer. This function is used only in conjunction with the init_buf -and init_buf_size params to relay_open(), and is typically used when -the ability to write into the channel at init-time is needed. The -basic usage is to specify an init_buf and init_buf_size to relay_open, -then call this function when it's safe to switch over to a normally -allocated channel buffer. 'Safe' means that the caller is in a -context that can sleep and that nothing is actively writing to the -channel. Returns 0 if successful, negative otherwise. - - -Writing directly into the channel -================================= - -Using the relay_write() API function as described above is the -preferred means of writing into a channel. In some cases, however, -in-kernel clients might want to write directly into a relay channel -rather than have relay_write() copy it into the buffer on the client's -behalf. Clients wishing to do this should follow the model used to -implement relay_write itself. The general sequence is: - -- get a pointer to the channel via rchan_get(). This increments the - channel's reference count. -- call relay_lock_channel(). This will perform the proper locking for - the channel given the scheme in use and the SMP usage. -- reserve a slot in the channel via relay_reserve() -- write directly to the reserved address -- call relay_commit() to commit the write -- call relay_unlock_channel() -- call rchan_put() to release the channel reference - -In particular, clients should make sure they call rchan_get() and -rchan_put() and not hold on to references to the channel pointer. -Also, forgetting to use relay_lock_channel()/relay_unlock_channel() -has no effect if the lockless scheme is being used, but could result -in corrupted buffer contents if the locking scheme is used. - - -Limitations -=========== - -Writes made via the write() system call are currently limited to 2 -pages worth of data. There is no such limit on the in-kernel API -function relay_write(). - -User applications can currently only mmap the complete buffer (it -doesn't really make sense to mmap only part of it, given its purpose). - - -Latest version -============== - -The latest version can be found at: - -http://www.opersys.com/relayfs - -Example relayfs clients, such as dynamic printk and the Linux Trace -Toolkit, can also be found there. - - -Credits -======= - -The ideas and specs for relayfs came about as a result of discussions -on tracing involving the following: - -Michel Dagenais -Richard Moore -Bob Wisniewski -Karim Yaghmour -Tom Zanussi - -Also thanks to Hubertus Franke for a lot of useful suggestions and bug -reports, and for contributing the klog code. diff --git a/Documentation/sound/alsa/Audigy-mixer.txt b/Documentation/sound/alsa/Audigy-mixer.txt new file mode 100644 index 000000000..5132fd95e --- /dev/null +++ b/Documentation/sound/alsa/Audigy-mixer.txt @@ -0,0 +1,345 @@ + + Sound Blaster Audigy mixer / default DSP code + =========================================== + +This is based on SB-Live-mixer.txt. + +The EMU10K2 chips have a DSP part which can be programmed to support +various ways of sample processing, which is described here. +(This acticle does not deal with the overall functionality of the +EMU10K2 chips. See the manuals section for further details.) + +The ALSA driver programs this portion of chip by default code +(can be altered later) which offers the following functionality: + + +1) Digital mixer controls +------------------------- + +These controls are built using the DSP instructions. They offer extended +functionality. Only the default build-in code in the ALSA driver is described +here. Note that the controls work as attenuators: the maximum value is the +neutral position leaving the signal unchanged. Note that if the same destination +is mentioned in multiple controls, the signal is accumulated and can be wrapped +(set to maximal or minimal value without checking of overflow). + + +Explanation of used abbreviations: + +DAC - digital to analog converter +ADC - analog to digital converter +I2S - one-way three wire serial bus for digital sound by Philips Semiconductors + (this standard is used for connecting standalone DAC and ADC converters) +LFE - low frequency effects (subwoofer signal) +AC97 - a chip containing an analog mixer, DAC and ADC converters +IEC958 - S/PDIF +FX-bus - the EMU10K2 chip has an effect bus containing 64 accumulators. + Each of the synthesizer voices can feed its output to these accumulators + and the DSP microcontroller can operate with the resulting sum. + +name='PCM Front Playback Volume',index=0 + +This control is used to attenuate samples for left and right front PCM FX-bus +accumulators. ALSA uses accumulators 8 and 9 for left and right front PCM +samples for 5.1 playback. The result samples are forwarded to the front DAC PCM +slots of the Philips DAC. + +name='PCM Surround Playback Volume',index=0 + +This control is used to attenuate samples for left and right surround PCM FX-bus +accumulators. ALSA uses accumulators 2 and 3 for left and right surround PCM +samples for 5.1 playback. The result samples are forwarded to the surround DAC PCM +slots of the Philips DAC. + +name='PCM Center Playback Volume',index=0 + +This control is used to attenuate samples for center PCM FX-bus accumulator. +ALSA uses accumulator 6 for center PCM sample for 5.1 playback. The result sample +is forwarded to the center DAC PCM slot of the Philips DAC. + +name='PCM LFE Playback Volume',index=0 + +This control is used to attenuate sample for LFE PCM FX-bus accumulator. +ALSA uses accumulator 7 for LFE PCM sample for 5.1 playback. The result sample +is forwarded to the LFE DAC PCM slot of the Philips DAC. + +name='PCM Playback Volume',index=0 + +This control is used to attenuate samples for left and right PCM FX-bus +accumulators. ALSA uses accumulators 0 and 1 for left and right PCM samples for +stereo playback. The result samples are forwarded to the front DAC PCM slots +of the Philips DAC. + +name='PCM Capture Volume',index=0 + +This control is used to attenuate samples for left and right PCM FX-bus +accumulator. ALSA uses accumulators 0 and 1 for left and right PCM. +The result is forwarded to the ADC capture FIFO (thus to the standard capture +PCM device). + +name='Music Playback Volume',index=0 + +This control is used to attenuate samples for left and right MIDI FX-bus +accumulators. ALSA uses accumulators 4 and 5 for left and right MIDI samples. +The result samples are forwarded to the front DAC PCM slots of the AC97 codec. + +name='Music Capture Volume',index=0 + +These controls are used to attenuate samples for left and right MIDI FX-bus +accumulator. ALSA uses accumulators 4 and 5 for left and right PCM. +The result is forwarded to the ADC capture FIFO (thus to the standard capture +PCM device). + +name='Mic Playback Volume',index=0 + +This control is used to attenuate samples for left and right Mic input. +For Mic input is used AC97 codec. The result samples are forwarded to +the front DAC PCM slots of the Philips DAC. Samples are forwarded to Mic +capture FIFO (device 1 - 16bit/8KHz mono) too without volume control. + +name='Mic Capture Volume',index=0 + +This control is used to attenuate samples for left and right Mic input. +The result is forwarded to the ADC capture FIFO (thus to the standard capture +PCM device). + +name='Audigy CD Playback Volume',index=0 + +This control is used to attenuate samples from left and right IEC958 TTL +digital inputs (usually used by a CDROM drive). The result samples are +forwarded to the front DAC PCM slots of the Philips DAC. + +name='Audigy CD Capture Volume',index=0 + +This control is used to attenuate samples from left and right IEC958 TTL +digital inputs (usually used by a CDROM drive). The result samples are +forwarded to the ADC capture FIFO (thus to the standard capture PCM device). + +name='IEC958 Optical Playback Volume',index=0 + +This control is used to attenuate samples from left and right IEC958 optical +digital input. The result samples are forwarded to the front DAC PCM slots +of the Philips DAC. + +name='IEC958 Optical Capture Volume',index=0 + +This control is used to attenuate samples from left and right IEC958 optical +digital inputs. The result samples are forwarded to the ADC capture FIFO +(thus to the standard capture PCM device). + +name='Line2 Playback Volume',index=0 + +This control is used to attenuate samples from left and right I2S ADC +inputs (on the AudigyDrive). The result samples are forwarded to the front +DAC PCM slots of the Philips DAC. + +name='Line2 Capture Volume',index=1 + +This control is used to attenuate samples from left and right I2S ADC +inputs (on the AudigyDrive). The result samples are forwarded to the ADC +capture FIFO (thus to the standard capture PCM device). + +name='Analog Mix Playback Volume',index=0 + +This control is used to attenuate samples from left and right I2S ADC +inputs from Philips ADC. The result samples are forwarded to the front +DAC PCM slots of the Philips DAC. This contains mix from analog sources +like CD, Line In, Aux, .... + +name='Analog Mix Capture Volume',index=1 + +This control is used to attenuate samples from left and right I2S ADC +inputs Philips ADC. The result samples are forwarded to the ADC +capture FIFO (thus to the standard capture PCM device). + +name='Aux2 Playback Volume',index=0 + +This control is used to attenuate samples from left and right I2S ADC +inputs (on the AudigyDrive). The result samples are forwarded to the front +DAC PCM slots of the Philips DAC. + +name='Aux2 Capture Volume',index=1 + +This control is used to attenuate samples from left and right I2S ADC +inputs (on the AudigyDrive). The result samples are forwarded to the ADC +capture FIFO (thus to the standard capture PCM device). + +name='Front Playback Volume',index=0 + +All stereo signals are mixed together and mirrored to surround, center and LFE. +This control is used to attenuate samples for left and right front speakers of +this mix. + +name='Surround Playback Volume',index=0 + +All stereo signals are mixed together and mirrored to surround, center and LFE. +This control is used to attenuate samples for left and right surround speakers of +this mix. + +name='Center Playback Volume',index=0 + +All stereo signals are mixed together and mirrored to surround, center and LFE. +This control is used to attenuate sample for center speaker of this mix. + +name='LFE Playback Volume',index=0 + +All stereo signals are mixed together and mirrored to surround, center and LFE. +This control is used to attenuate sample for LFE speaker of this mix. + +name='Tone Control - Switch',index=0 + +This control turns the tone control on or off. The samples for front, rear +and center / LFE outputs are affected. + +name='Tone Control - Bass',index=0 + +This control sets the bass intensity. There is no neutral value!! +When the tone control code is activated, the samples are always modified. +The closest value to pure signal is 20. + +name='Tone Control - Treble',index=0 + +This control sets the treble intensity. There is no neutral value!! +When the tone control code is activated, the samples are always modified. +The closest value to pure signal is 20. + +name='Master Playback Volume',index=0 + +This control is used to attenuate samples for front, surround, center and +LFE outputs. + +name='IEC958 Optical Raw Playback Switch',index=0 + +If this switch is on, then the samples for the IEC958 (S/PDIF) digital +output are taken only from the raw FX8010 PCM, otherwise standard front +PCM samples are taken. + + +2) PCM stream related controls +------------------------------ + +name='EMU10K1 PCM Volume',index 0-31 + +Channel volume attenuation in range 0-0xffff. The maximum value (no +attenuation) is default. The channel mapping for three values is +as follows: + + 0 - mono, default 0xffff (no attenuation) + 1 - left, default 0xffff (no attenuation) + 2 - right, default 0xffff (no attenuation) + +name='EMU10K1 PCM Send Routing',index 0-31 + +This control specifies the destination - FX-bus accumulators. There 24 +values with this mapping: + + 0 - mono, A destination (FX-bus 0-63), default 0 + 1 - mono, B destination (FX-bus 0-63), default 1 + 2 - mono, C destination (FX-bus 0-63), default 2 + 3 - mono, D destination (FX-bus 0-63), default 3 + 4 - mono, E destination (FX-bus 0-63), default 0 + 5 - mono, F destination (FX-bus 0-63), default 0 + 6 - mono, G destination (FX-bus 0-63), default 0 + 7 - mono, H destination (FX-bus 0-63), default 0 + 8 - left, A destination (FX-bus 0-63), default 0 + 9 - left, B destination (FX-bus 0-63), default 1 + 10 - left, C destination (FX-bus 0-63), default 2 + 11 - left, D destination (FX-bus 0-63), default 3 + 12 - left, E destination (FX-bus 0-63), default 0 + 13 - left, F destination (FX-bus 0-63), default 0 + 14 - left, G destination (FX-bus 0-63), default 0 + 15 - left, H destination (FX-bus 0-63), default 0 + 16 - right, A destination (FX-bus 0-63), default 0 + 17 - right, B destination (FX-bus 0-63), default 1 + 18 - right, C destination (FX-bus 0-63), default 2 + 19 - right, D destination (FX-bus 0-63), default 3 + 20 - right, E destination (FX-bus 0-63), default 0 + 21 - right, F destination (FX-bus 0-63), default 0 + 22 - right, G destination (FX-bus 0-63), default 0 + 23 - right, H destination (FX-bus 0-63), default 0 + +Don't forget that it's illegal to assign a channel to the same FX-bus accumulator +more than once (it means 0=0 && 1=0 is an invalid combination). + +name='EMU10K1 PCM Send Volume',index 0-31 + +It specifies the attenuation (amount) for given destination in range 0-255. +The channel mapping is following: + + 0 - mono, A destination attn, default 255 (no attenuation) + 1 - mono, B destination attn, default 255 (no attenuation) + 2 - mono, C destination attn, default 0 (mute) + 3 - mono, D destination attn, default 0 (mute) + 4 - mono, E destination attn, default 0 (mute) + 5 - mono, F destination attn, default 0 (mute) + 6 - mono, G destination attn, default 0 (mute) + 7 - mono, H destination attn, default 0 (mute) + 8 - left, A destination attn, default 255 (no attenuation) + 9 - left, B destination attn, default 0 (mute) + 10 - left, C destination attn, default 0 (mute) + 11 - left, D destination attn, default 0 (mute) + 12 - left, E destination attn, default 0 (mute) + 13 - left, F destination attn, default 0 (mute) + 14 - left, G destination attn, default 0 (mute) + 15 - left, H destination attn, default 0 (mute) + 16 - right, A destination attn, default 0 (mute) + 17 - right, B destination attn, default 255 (no attenuation) + 18 - right, C destination attn, default 0 (mute) + 19 - right, D destination attn, default 0 (mute) + 20 - right, E destination attn, default 0 (mute) + 21 - right, F destination attn, default 0 (mute) + 22 - right, G destination attn, default 0 (mute) + 23 - right, H destination attn, default 0 (mute) + + + +4) MANUALS/PATENTS: +------------------- + +ftp://opensource.creative.com/pub/doc +------------------------------------- + + Files: + LM4545.pdf AC97 Codec + + m2049.pdf The EMU10K1 Digital Audio Processor + + hog63.ps FX8010 - A DSP Chip Architecture for Audio Effects + + +WIPO Patents +------------ + Patent numbers: + WO 9901813 (A1) Audio Effects Processor with multiple asynchronous (Jan. 14, 1999) + streams + + WO 9901814 (A1) Processor with Instruction Set for Audio Effects (Jan. 14, 1999) + + WO 9901953 (A1) Audio Effects Processor having Decoupled Instruction + Execution and Audio Data Sequencing (Jan. 14, 1999) + + +US Patents (http://www.uspto.gov/) +---------------------------------- + + US 5925841 Digital Sampling Instrument employing cache memory (Jul. 20, 1999) + + US 5928342 Audio Effects Processor integrated on a single chip (Jul. 27, 1999) + with a multiport memory onto which multiple asynchronous + digital sound samples can be concurrently loaded + + US 5930158 Processor with Instruction Set for Audio Effects (Jul. 27, 1999) + + US 6032235 Memory initialization circuit (Tram) (Feb. 29, 2000) + + US 6138207 Interpolation looping of audio samples in cache connected to (Oct. 24, 2000) + system bus with prioritization and modification of bus transfers + in accordance with loop ends and minimum block sizes + + US 6151670 Method for conserving memory storage using a (Nov. 21, 2000) + pool of short term memory registers + + US 6195715 Interrupt control for multiple programs communicating with (Feb. 27, 2001) + a common interrupt by associating programs to GP registers, + defining interrupt register, polling GP registers, and invoking + callback routine associated with defined interrupt register diff --git a/arch/arm/configs/ixp4xx_defconfig b/arch/arm/configs/ixp4xx_defconfig new file mode 100644 index 000000000..fd95f39bc --- /dev/null +++ b/arch/arm/configs/ixp4xx_defconfig @@ -0,0 +1,1081 @@ +# +# Automatically generated make config: don't edit +# +CONFIG_ARM=y +CONFIG_MMU=y +CONFIG_UID16=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y +CONFIG_CLEAN_COMPILE=y +CONFIG_STANDALONE=y +CONFIG_BROKEN_ON_SMP=y + +# +# General setup +# +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +# CONFIG_POSIX_MQUEUE is not set +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_SYSCTL=y +# CONFIG_AUDIT is not set +CONFIG_LOG_BUF_SHIFT=14 +# CONFIG_HOTPLUG is not set +# CONFIG_IKCONFIG is not set +CONFIG_EMBEDDED=y +CONFIG_KALLSYMS=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_AS=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +CONFIG_CC_OPTIMIZE_FOR_SIZE=y + +# +# Loadable module support +# +CONFIG_MODULES=y +# CONFIG_MODULE_UNLOAD is not set +CONFIG_OBSOLETE_MODPARM=y +CONFIG_MODVERSIONS=y +CONFIG_KMOD=y + +# +# System Type +# +# CONFIG_ARCH_ADIFCC is not set +# CONFIG_ARCH_CLPS7500 is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_CO285 is not set +# CONFIG_ARCH_PXA is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_CAMELOT is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_IOP3XX is not set +CONFIG_ARCH_IXP4XX=y +# CONFIG_ARCH_L7200 is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_SHARK is not set +# CONFIG_ARCH_S3C2410 is not set +# CONFIG_ARCH_OMAP is not set +# CONFIG_ARCH_LH7A40X is not set +# CONFIG_ARCH_VERSATILE_PB is not set + +# +# CLPS711X/EP721X Implementations +# + +# +# Epxa10db +# + +# +# Footbridge Implementations +# + +# +# IOP3xx Implementation Options +# +# CONFIG_ARCH_IOP310 is not set +# CONFIG_ARCH_IOP321 is not set + +# +# IOP3xx Chipset Features +# +CONFIG_ARCH_SUPPORTS_BIG_ENDIAN=y + +# +# Intel IXP4xx Implementation Options +# + +# +# IXP4xx Platforms +# +CONFIG_ARCH_IXDP425=y +CONFIG_ARCH_IXCDP1100=y +CONFIG_ARCH_PRPMC1100=y +CONFIG_ARCH_ADI_COYOTE=y +# CONFIG_ARCH_AVILA is not set +CONFIG_ARCH_IXDP4XX=y + +# +# IXP4xx Options +# +# CONFIG_IXP4XX_INDIRECT_PCI is not set + +# +# Intel PXA250/210 Implementations +# + +# +# SA11x0 Implementations +# + +# +# TI OMAP Implementations +# + +# +# OMAP Core Type +# + +# +# OMAP Board Type +# + +# +# OMAP Feature Selections +# + +# +# S3C2410 Implementations +# + +# +# LH7A40X Implementations +# +CONFIG_DMABOUNCE=y + +# +# Processor Type +# +CONFIG_CPU_32=y +CONFIG_CPU_XSCALE=y +CONFIG_CPU_32v5=y +CONFIG_CPU_ABRT_EV5T=y +CONFIG_CPU_TLB_V4WBI=y +CONFIG_CPU_MINICACHE=y + +# +# Processor Features +# +# CONFIG_ARM_THUMB is not set +CONFIG_CPU_BIG_ENDIAN=y +CONFIG_XSCALE_PMU=y + +# +# General setup +# +CONFIG_PCI=y +# CONFIG_ZBOOT_ROM is not set +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_PCI_LEGACY_PROC=y +CONFIG_PCI_NAMES=y + +# +# At least one math emulation must be selected +# +CONFIG_FPE_NWFPE=y +# CONFIG_FPE_NWFPE_XP is not set +# CONFIG_FPE_FASTFPE is not set +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_AOUT is not set +# CONFIG_BINFMT_MISC is not set + +# +# Generic Driver Options +# +# CONFIG_DEBUG_DRIVER is not set +CONFIG_PM=y +# CONFIG_PREEMPT is not set +CONFIG_APM=y +# CONFIG_ARTHUR is not set +CONFIG_CMDLINE="console=ttyS0,115200 ip=bootp root=/dev/nfs" +CONFIG_ALIGNMENT_TRAP=y + +# +# Parallel port support +# +# CONFIG_PARPORT is not set + +# +# Memory Technology Devices (MTD) +# +CONFIG_MTD=y +# CONFIG_MTD_DEBUG is not set +CONFIG_MTD_PARTITIONS=y +# CONFIG_MTD_CONCAT is not set +CONFIG_MTD_REDBOOT_PARTS=y +# CONFIG_MTD_CMDLINE_PARTS is not set +# CONFIG_MTD_AFS_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set + +# +# RAM/ROM/Flash chip drivers +# +CONFIG_MTD_CFI=y +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_GEN_PROBE=y +# CONFIG_MTD_CFI_ADV_OPTIONS is not set +CONFIG_MTD_CFI_INTELEXT=y +# CONFIG_MTD_CFI_AMDSTD is not set +# CONFIG_MTD_CFI_STAA is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set +# CONFIG_MTD_OBSOLETE_CHIPS is not set + +# +# Mapping drivers for chip access +# +CONFIG_MTD_COMPLEX_MAPPINGS=y +# CONFIG_MTD_PHYSMAP is not set +# CONFIG_MTD_ARM_INTEGRATOR is not set +CONFIG_MTD_IXP4XX=y +# CONFIG_MTD_EDB7312 is not set +# CONFIG_MTD_PCI is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_PMC551 is not set +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLKMTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOC2001PLUS is not set + +# +# NAND Flash Device Drivers +# +CONFIG_MTD_NAND=m +# CONFIG_MTD_NAND_VERIFY_WRITE is not set +CONFIG_MTD_NAND_IDS=m + +# +# Plug and Play support +# + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +# CONFIG_BLK_CPQ_DA is not set +# CONFIG_BLK_CPQ_CISS_DA is not set +# CONFIG_BLK_DEV_DAC960 is not set +# CONFIG_BLK_DEV_UMEM is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_CRYPTOLOOP is not set +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_CARMEL is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=8192 +CONFIG_BLK_DEV_INITRD=y + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set + +# +# Networking support +# +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=m +CONFIG_PACKET_MMAP=y +CONFIG_NETLINK_DEV=m +CONFIG_UNIX=y +# CONFIG_NET_KEY is not set +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_ROUTE_FWMARK=y +CONFIG_IP_ROUTE_NAT=y +CONFIG_IP_ROUTE_MULTIPATH=y +CONFIG_IP_ROUTE_TOS=y +CONFIG_IP_ROUTE_VERBOSE=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +# CONFIG_IP_PNP_RARP is not set +CONFIG_NET_IPIP=m +CONFIG_NET_IPGRE=m +CONFIG_NET_IPGRE_BROADCAST=y +CONFIG_IP_MROUTE=y +CONFIG_IP_PIMSM_V1=y +CONFIG_IP_PIMSM_V2=y +# CONFIG_ARPD is not set +CONFIG_SYN_COOKIES=y +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set + +# +# IP: Virtual Server Configuration +# +CONFIG_IP_VS=m +CONFIG_IP_VS_DEBUG=y +CONFIG_IP_VS_TAB_BITS=12 + +# +# IPVS transport protocol load balancing support +# +# CONFIG_IP_VS_PROTO_TCP is not set +# CONFIG_IP_VS_PROTO_UDP is not set +# CONFIG_IP_VS_PROTO_ESP is not set +# CONFIG_IP_VS_PROTO_AH is not set + +# +# IPVS scheduler +# +CONFIG_IP_VS_RR=m +CONFIG_IP_VS_WRR=m +CONFIG_IP_VS_LC=m +CONFIG_IP_VS_WLC=m +CONFIG_IP_VS_LBLC=m +CONFIG_IP_VS_LBLCR=m +CONFIG_IP_VS_DH=m +CONFIG_IP_VS_SH=m +# CONFIG_IP_VS_SED is not set +# CONFIG_IP_VS_NQ is not set + +# +# IPVS application helper +# +# CONFIG_IPV6 is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +CONFIG_BRIDGE_NETFILTER=y + +# +# IP: Netfilter Configuration +# +CONFIG_IP_NF_CONNTRACK=m +CONFIG_IP_NF_FTP=m +CONFIG_IP_NF_IRC=m +# CONFIG_IP_NF_TFTP is not set +# CONFIG_IP_NF_AMANDA is not set +CONFIG_IP_NF_QUEUE=m +CONFIG_IP_NF_IPTABLES=m +CONFIG_IP_NF_MATCH_LIMIT=m +# CONFIG_IP_NF_MATCH_IPRANGE is not set +CONFIG_IP_NF_MATCH_MAC=m +# CONFIG_IP_NF_MATCH_PKTTYPE is not set +CONFIG_IP_NF_MATCH_MARK=m +CONFIG_IP_NF_MATCH_MULTIPORT=m +CONFIG_IP_NF_MATCH_TOS=m +# CONFIG_IP_NF_MATCH_RECENT is not set +# CONFIG_IP_NF_MATCH_ECN is not set +# CONFIG_IP_NF_MATCH_DSCP is not set +CONFIG_IP_NF_MATCH_AH_ESP=m +CONFIG_IP_NF_MATCH_LENGTH=m +CONFIG_IP_NF_MATCH_TTL=m +CONFIG_IP_NF_MATCH_TCPMSS=m +# CONFIG_IP_NF_MATCH_HELPER is not set +CONFIG_IP_NF_MATCH_STATE=m +# CONFIG_IP_NF_MATCH_CONNTRACK is not set +CONFIG_IP_NF_MATCH_OWNER=m +# CONFIG_IP_NF_MATCH_PHYSDEV is not set +CONFIG_IP_NF_FILTER=m +CONFIG_IP_NF_TARGET_REJECT=m +CONFIG_IP_NF_NAT=m +CONFIG_IP_NF_NAT_NEEDED=y +CONFIG_IP_NF_TARGET_MASQUERADE=m +CONFIG_IP_NF_TARGET_REDIRECT=m +# CONFIG_IP_NF_TARGET_NETMAP is not set +# CONFIG_IP_NF_TARGET_SAME is not set +CONFIG_IP_NF_NAT_LOCAL=y +CONFIG_IP_NF_NAT_SNMP_BASIC=m +CONFIG_IP_NF_NAT_IRC=m +CONFIG_IP_NF_NAT_FTP=m +CONFIG_IP_NF_MANGLE=m +CONFIG_IP_NF_TARGET_TOS=m +# CONFIG_IP_NF_TARGET_ECN is not set +# CONFIG_IP_NF_TARGET_DSCP is not set +CONFIG_IP_NF_TARGET_MARK=m +# CONFIG_IP_NF_TARGET_CLASSIFY is not set +CONFIG_IP_NF_TARGET_LOG=m +CONFIG_IP_NF_TARGET_ULOG=m +CONFIG_IP_NF_TARGET_TCPMSS=m +CONFIG_IP_NF_ARPTABLES=m +CONFIG_IP_NF_ARPFILTER=m +# CONFIG_IP_NF_ARP_MANGLE is not set +CONFIG_IP_NF_COMPAT_IPCHAINS=m +CONFIG_IP_NF_COMPAT_IPFWADM=m +# CONFIG_IP_NF_RAW is not set + +# +# Bridge: Netfilter Configuration +# +# CONFIG_BRIDGE_NF_EBTABLES is not set +CONFIG_XFRM=y +# CONFIG_XFRM_USER is not set + +# +# SCTP Configuration (EXPERIMENTAL) +# +# CONFIG_IP_SCTP is not set +CONFIG_ATM=y +CONFIG_ATM_CLIP=y +# CONFIG_ATM_CLIP_NO_ICMP is not set +CONFIG_ATM_LANE=m +CONFIG_ATM_MPOA=m +CONFIG_ATM_BR2684=m +# CONFIG_ATM_BR2684_IPFILTER is not set +CONFIG_BRIDGE=m +CONFIG_VLAN_8021Q=m +# CONFIG_DECNET is not set +CONFIG_LLC=m +# CONFIG_LLC2 is not set +CONFIG_IPX=m +# CONFIG_IPX_INTERN is not set +CONFIG_ATALK=m +CONFIG_DEV_APPLETALK=y +CONFIG_IPDDP=m +CONFIG_IPDDP_ENCAP=y +CONFIG_IPDDP_DECAP=y +CONFIG_X25=m +CONFIG_LAPB=m +# CONFIG_NET_DIVERT is not set +CONFIG_ECONET=m +CONFIG_ECONET_AUNUDP=y +CONFIG_ECONET_NATIVE=y +CONFIG_WAN_ROUTER=m +# CONFIG_NET_FASTROUTE is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_CBQ=m +CONFIG_NET_SCH_HTB=m +# CONFIG_NET_SCH_HFSC is not set +CONFIG_NET_SCH_CSZ=m +# CONFIG_NET_SCH_ATM is not set +CONFIG_NET_SCH_PRIO=m +CONFIG_NET_SCH_RED=m +CONFIG_NET_SCH_SFQ=m +CONFIG_NET_SCH_TEQL=m +CONFIG_NET_SCH_TBF=m +CONFIG_NET_SCH_GRED=m +CONFIG_NET_SCH_DSMARK=m +# CONFIG_NET_SCH_DELAY is not set +CONFIG_NET_SCH_INGRESS=m +CONFIG_NET_QOS=y +CONFIG_NET_ESTIMATOR=y +CONFIG_NET_CLS=y +CONFIG_NET_CLS_TCINDEX=m +CONFIG_NET_CLS_ROUTE4=m +CONFIG_NET_CLS_ROUTE=y +CONFIG_NET_CLS_FW=m +CONFIG_NET_CLS_U32=m +CONFIG_NET_CLS_RSVP=m +CONFIG_NET_CLS_RSVP6=m +CONFIG_NET_CLS_POLICE=y + +# +# Network testing +# +CONFIG_NET_PKTGEN=m +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_HAMRADIO is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +CONFIG_NETDEVICES=y +CONFIG_DUMMY=y +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set +# CONFIG_ETHERTAP is not set + +# +# ARCnet devices +# +# CONFIG_ARCNET is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +CONFIG_MII=y +# CONFIG_HAPPYMEAL is not set +# CONFIG_SUNGEM is not set +# CONFIG_NET_VENDOR_3COM is not set + +# +# Tulip family network device support +# +# CONFIG_NET_TULIP is not set +# CONFIG_HP100 is not set +CONFIG_NET_PCI=y +# CONFIG_PCNET32 is not set +# CONFIG_AMD8111_ETH is not set +# CONFIG_ADAPTEC_STARFIRE is not set +# CONFIG_B44 is not set +# CONFIG_FORCEDETH is not set +# CONFIG_DGRS is not set +CONFIG_EEPRO100=y +# CONFIG_EEPRO100_PIO is not set +# CONFIG_E100 is not set +# CONFIG_FEALNX is not set +# CONFIG_NATSEMI is not set +# CONFIG_NE2K_PCI is not set +# CONFIG_8139CP is not set +# CONFIG_8139TOO is not set +# CONFIG_SIS900 is not set +# CONFIG_EPIC100 is not set +# CONFIG_SUNDANCE is not set +# CONFIG_TLAN is not set +# CONFIG_VIA_RHINE is not set + +# +# Ethernet (1000 Mbit) +# +# CONFIG_ACENIC is not set +# CONFIG_DL2K is not set +# CONFIG_E1000 is not set +# CONFIG_NS83820 is not set +# CONFIG_HAMACHI is not set +# CONFIG_YELLOWFIN is not set +# CONFIG_R8169 is not set +# CONFIG_SK98LIN is not set +# CONFIG_TIGON3 is not set + +# +# Ethernet (10000 Mbit) +# +# CONFIG_IXGB is not set +# CONFIG_S2IO is not set + +# +# Token Ring devices +# +# CONFIG_TR is not set + +# +# Wireless LAN (non-hamradio) +# +CONFIG_NET_RADIO=y + +# +# Obsolete Wireless cards support (pre-802.11) +# +# CONFIG_STRIP is not set + +# +# Wireless 802.11b ISA/PCI cards support +# +# CONFIG_AIRO is not set +CONFIG_HERMES=y +# CONFIG_PLX_HERMES is not set +# CONFIG_TMD_HERMES is not set +CONFIG_PCI_HERMES=y +# CONFIG_ATMEL is not set + +# +# Prism GT/Duette 802.11(a/b/g) PCI/Cardbus support +# +CONFIG_NET_WIRELESS=y + +# +# Wan interfaces +# +CONFIG_WAN=y +# CONFIG_DSCC4 is not set +# CONFIG_LANMEDIA is not set +# CONFIG_SYNCLINK_SYNCPPP is not set +CONFIG_HDLC=m +CONFIG_HDLC_RAW=y +# CONFIG_HDLC_RAW_ETH is not set +CONFIG_HDLC_CISCO=y +CONFIG_HDLC_FR=y +CONFIG_HDLC_PPP=y +CONFIG_HDLC_X25=y +# CONFIG_PCI200SYN is not set +# CONFIG_WANXL is not set +# CONFIG_PC300 is not set +# CONFIG_FARSYNC is not set +CONFIG_DLCI=m +CONFIG_DLCI_COUNT=24 +CONFIG_DLCI_MAX=8 +CONFIG_WAN_ROUTER_DRIVERS=y +# CONFIG_CYCLADES_SYNC is not set +# CONFIG_LAPBETHER is not set +# CONFIG_X25_ASY is not set + +# +# ATM drivers +# +CONFIG_ATM_TCP=m +# CONFIG_ATM_LANAI is not set +# CONFIG_ATM_ENI is not set +# CONFIG_ATM_FIRESTREAM is not set +# CONFIG_ATM_ZATM is not set +# CONFIG_ATM_NICSTAR is not set +# CONFIG_ATM_IDT77252 is not set +# CONFIG_ATM_AMBASSADOR is not set +# CONFIG_ATM_HORIZON is not set +# CONFIG_ATM_IA is not set +# CONFIG_ATM_FORE200E_MAYBE is not set +# CONFIG_ATM_HE is not set +# CONFIG_FDDI is not set +# CONFIG_HIPPI is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set +# CONFIG_RCPCI is not set +# CONFIG_SHAPER is not set +# CONFIG_NETCONSOLE is not set + +# +# ATA/ATAPI/MFM/RLL support +# +CONFIG_IDE=y +CONFIG_BLK_DEV_IDE=y + +# +# Please see Documentation/ide.txt for help/info on IDE drives +# +CONFIG_BLK_DEV_IDEDISK=y +# CONFIG_IDEDISK_MULTI_MODE is not set +# CONFIG_IDEDISK_STROKE is not set +# CONFIG_BLK_DEV_IDECD is not set +# CONFIG_BLK_DEV_IDETAPE is not set +# CONFIG_BLK_DEV_IDEFLOPPY is not set +# CONFIG_IDE_TASK_IOCTL is not set +# CONFIG_IDE_TASKFILE_IO is not set + +# +# IDE chipset support/bugfixes +# +CONFIG_IDE_GENERIC=y +CONFIG_BLK_DEV_IDEPCI=y +# CONFIG_IDEPCI_SHARE_IRQ is not set +# CONFIG_BLK_DEV_OFFBOARD is not set +# CONFIG_BLK_DEV_GENERIC is not set +# CONFIG_BLK_DEV_OPTI621 is not set +# CONFIG_BLK_DEV_SL82C105 is not set +CONFIG_BLK_DEV_IDEDMA_PCI=y +# CONFIG_BLK_DEV_IDEDMA_FORCED is not set +# CONFIG_IDEDMA_PCI_AUTO is not set +CONFIG_BLK_DEV_ADMA=y +# CONFIG_BLK_DEV_AEC62XX is not set +# CONFIG_BLK_DEV_ALI15X3 is not set +# CONFIG_BLK_DEV_AMD74XX is not set +CONFIG_BLK_DEV_CMD64X=y +# CONFIG_BLK_DEV_TRIFLEX is not set +# CONFIG_BLK_DEV_CY82C693 is not set +# CONFIG_BLK_DEV_CS5520 is not set +# CONFIG_BLK_DEV_CS5530 is not set +# CONFIG_BLK_DEV_HPT34X is not set +CONFIG_BLK_DEV_HPT366=y +# CONFIG_BLK_DEV_SC1200 is not set +# CONFIG_BLK_DEV_PIIX is not set +# CONFIG_BLK_DEV_NS87415 is not set +# CONFIG_BLK_DEV_PDC202XX_OLD is not set +CONFIG_BLK_DEV_PDC202XX_NEW=y +# CONFIG_PDC202XX_FORCE is not set +# CONFIG_BLK_DEV_SVWKS is not set +# CONFIG_BLK_DEV_SIIMAGE is not set +# CONFIG_BLK_DEV_SLC90E66 is not set +# CONFIG_BLK_DEV_TRM290 is not set +# CONFIG_BLK_DEV_VIA82CXXX is not set +CONFIG_BLK_DEV_IDEDMA=y +# CONFIG_IDEDMA_IVB is not set +# CONFIG_IDEDMA_AUTO is not set +# CONFIG_BLK_DEV_HD is not set + +# +# SCSI device support +# +# CONFIG_SCSI is not set + +# +# Fusion MPT device support +# +# CONFIG_FUSION is not set + +# +# IEEE 1394 (FireWire) support +# +# CONFIG_IEEE1394 is not set + +# +# I2O device support +# +# CONFIG_I2O is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Input device support +# +CONFIG_INPUT=y + +# +# Userland interfaces +# +CONFIG_INPUT_MOUSEDEV=y +CONFIG_INPUT_MOUSEDEV_PSAUX=y +CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_TSDEV is not set +# CONFIG_INPUT_EVDEV is not set +# CONFIG_INPUT_EVBUG is not set + +# +# Input I/O drivers +# +# CONFIG_GAMEPORT is not set +CONFIG_SOUND_GAMEPORT=y +# CONFIG_SERIO is not set +# CONFIG_SERIO_I8042 is not set + +# +# Input Device Drivers +# +# CONFIG_INPUT_KEYBOARD is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set + +# +# Character devices +# +# CONFIG_VT is not set +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_NR_UARTS=2 +# CONFIG_SERIAL_8250_EXTENDED is not set + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_UNIX98_PTYS=y +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=256 +# CONFIG_QIC02_TAPE is not set + +# +# IPMI +# +# CONFIG_IPMI_HANDLER is not set + +# +# Watchdog Cards +# +CONFIG_WATCHDOG=y +# CONFIG_WATCHDOG_NOWAYOUT is not set + +# +# Watchdog Device Drivers +# +# CONFIG_SOFT_WATCHDOG is not set +CONFIG_IXP4XX_WATCHDOG=y + +# +# PCI-based Watchdog Cards +# +# CONFIG_PCIPCWATCHDOG is not set +# CONFIG_WDTPCI is not set +# CONFIG_NVRAM is not set +# CONFIG_RTC is not set +# CONFIG_GEN_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set +# CONFIG_AGP is not set +# CONFIG_DRM is not set +# CONFIG_RAW_DRIVER is not set + +# +# I2C support +# +CONFIG_I2C=y +CONFIG_I2C_CHARDEV=y + +# +# I2C Algorithms +# +CONFIG_I2C_ALGOBIT=y +# CONFIG_I2C_ALGOPCF is not set + +# +# I2C Hardware Bus support +# +# CONFIG_I2C_ALI1535 is not set +# CONFIG_I2C_ALI1563 is not set +# CONFIG_I2C_ALI15X3 is not set +# CONFIG_I2C_AMD756 is not set +# CONFIG_I2C_AMD8111 is not set +# CONFIG_I2C_I801 is not set +# CONFIG_I2C_I810 is not set +# CONFIG_I2C_ISA is not set +CONFIG_I2C_IXP4XX=y +# CONFIG_I2C_NFORCE2 is not set +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_PIIX4 is not set +# CONFIG_I2C_PROSAVAGE is not set +# CONFIG_I2C_SAVAGE4 is not set +# CONFIG_SCx200_ACB is not set +# CONFIG_I2C_SIS5595 is not set +# CONFIG_I2C_SIS630 is not set +# CONFIG_I2C_SIS96X is not set +# CONFIG_I2C_VIA is not set +# CONFIG_I2C_VIAPRO is not set +# CONFIG_I2C_VOODOO3 is not set + +# +# Hardware Sensors Chip support +# +CONFIG_I2C_SENSOR=y +# CONFIG_SENSORS_ADM1021 is not set +# CONFIG_SENSORS_ASB100 is not set +# CONFIG_SENSORS_DS1621 is not set +# CONFIG_SENSORS_FSCHER is not set +# CONFIG_SENSORS_GL518SM is not set +# CONFIG_SENSORS_IT87 is not set +# CONFIG_SENSORS_LM75 is not set +# CONFIG_SENSORS_LM78 is not set +# CONFIG_SENSORS_LM80 is not set +# CONFIG_SENSORS_LM83 is not set +# CONFIG_SENSORS_LM85 is not set +# CONFIG_SENSORS_LM90 is not set +# CONFIG_SENSORS_VIA686A is not set +# CONFIG_SENSORS_W83781D is not set +# CONFIG_SENSORS_W83L785TS is not set +# CONFIG_SENSORS_W83627HF is not set + +# +# Other I2C Chip support +# +CONFIG_SENSORS_EEPROM=y +# CONFIG_SENSORS_PCF8574 is not set +# CONFIG_SENSORS_PCF8591 is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +# CONFIG_I2C_DEBUG_CHIP is not set + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# Digital Video Broadcasting Devices +# +# CONFIG_DVB is not set + +# +# File systems +# +CONFIG_EXT2_FS=y +CONFIG_EXT2_FS_XATTR=y +CONFIG_EXT2_FS_POSIX_ACL=y +# CONFIG_EXT2_FS_SECURITY is not set +CONFIG_EXT3_FS=y +CONFIG_EXT3_FS_XATTR=y +CONFIG_EXT3_FS_POSIX_ACL=y +# CONFIG_EXT3_FS_SECURITY is not set +CONFIG_JBD=y +# CONFIG_JBD_DEBUG is not set +CONFIG_FS_MBCACHE=y +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +CONFIG_FS_POSIX_ACL=y +# CONFIG_XFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +# CONFIG_FAT_FS is not set +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_SYSFS=y +# CONFIG_DEVFS_FS is not set +# CONFIG_DEVPTS_FS_XATTR is not set +CONFIG_TMPFS=y +# CONFIG_HUGETLB_PAGE is not set +CONFIG_RAMFS=y + +# +# Miscellaneous filesystems +# +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_JFFS_FS is not set +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_FS_DEBUG=0 +# CONFIG_JFFS2_FS_NAND is not set +# CONFIG_CRAMFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set + +# +# Network File Systems +# +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +# CONFIG_NFS_V4 is not set +# CONFIG_NFS_DIRECTIO is not set +# CONFIG_NFSD is not set +CONFIG_ROOT_NFS=y +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +# CONFIG_EXPORTFS is not set +CONFIG_SUNRPC=y +# CONFIG_RPCSEC_GSS_KRB5 is not set +# CONFIG_SMB_FS is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_INTERMEZZO_FS is not set +# CONFIG_AFS_FS is not set + +# +# Partition Types +# +CONFIG_PARTITION_ADVANCED=y +# CONFIG_ACORN_PARTITION is not set +# CONFIG_OSF_PARTITION is not set +# CONFIG_AMIGA_PARTITION is not set +# CONFIG_ATARI_PARTITION is not set +# CONFIG_MAC_PARTITION is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_BSD_DISKLABEL is not set +# CONFIG_MINIX_SUBPARTITION is not set +# CONFIG_SOLARIS_X86_PARTITION is not set +# CONFIG_UNIXWARE_DISKLABEL is not set +# CONFIG_LDM_PARTITION is not set +# CONFIG_NEC98_PARTITION is not set +# CONFIG_SGI_PARTITION is not set +# CONFIG_ULTRIX_PARTITION is not set +# CONFIG_SUN_PARTITION is not set +# CONFIG_EFI_PARTITION is not set + +# +# Native Language Support +# +# CONFIG_NLS is not set + +# +# Profiling support +# +# CONFIG_PROFILING is not set + +# +# Graphics support +# +# CONFIG_FB is not set + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# Misc devices +# + +# +# USB support +# +# CONFIG_USB is not set + +# +# USB Gadget Support +# +# CONFIG_USB_GADGET is not set + +# +# Kernel hacking +# +CONFIG_FRAME_POINTER=y +# CONFIG_DEBUG_USER is not set +# CONFIG_DEBUG_INFO is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SLAB is not set +CONFIG_MAGIC_SYSRQ=y +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_WAITQ is not set +CONFIG_DEBUG_BUGVERBOSE=y +CONFIG_DEBUG_ERRORS=y +CONFIG_DEBUG_LL=y +# CONFIG_DEBUG_ICEDCC is not set +# CONFIG_DEBUG_BDI2000_XSCALE is not set + +# +# Security options +# +# CONFIG_SECURITY is not set + +# +# Cryptographic options +# +# CONFIG_CRYPTO is not set + +# +# Library routines +# +CONFIG_CRC32=y +# CONFIG_LIBCRC32C is not set +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y diff --git a/arch/arm/configs/mainstone_defconfig b/arch/arm/configs/mainstone_defconfig new file mode 100644 index 000000000..925b2777f --- /dev/null +++ b/arch/arm/configs/mainstone_defconfig @@ -0,0 +1,743 @@ +# +# Automatically generated make config: don't edit +# +CONFIG_ARM=y +CONFIG_MMU=y +CONFIG_UID16=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y +CONFIG_CLEAN_COMPILE=y +CONFIG_STANDALONE=y +CONFIG_BROKEN_ON_SMP=y + +# +# General setup +# +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +# CONFIG_POSIX_MQUEUE is not set +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_SYSCTL=y +# CONFIG_AUDIT is not set +CONFIG_LOG_BUF_SHIFT=14 +CONFIG_HOTPLUG=y +# CONFIG_IKCONFIG is not set +# CONFIG_EMBEDDED is not set +CONFIG_KALLSYMS=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_AS=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +CONFIG_CC_OPTIMIZE_FOR_SIZE=y + +# +# Loadable module support +# +CONFIG_MODULES=y +# CONFIG_MODULE_UNLOAD is not set +CONFIG_OBSOLETE_MODPARM=y +# CONFIG_MODVERSIONS is not set +# CONFIG_KMOD is not set + +# +# System Type +# +# CONFIG_ARCH_ADIFCC is not set +# CONFIG_ARCH_CLPS7500 is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_CO285 is not set +CONFIG_ARCH_PXA=y +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_CAMELOT is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_IOP3XX is not set +# CONFIG_ARCH_L7200 is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_SHARK is not set +# CONFIG_ARCH_S3C2410 is not set +# CONFIG_ARCH_OMAP is not set +# CONFIG_ARCH_LH7A40X is not set +# CONFIG_ARCH_VERSATILE_PB is not set + +# +# CLPS711X/EP721X Implementations +# + +# +# Epxa10db +# + +# +# Footbridge Implementations +# + +# +# IOP3xx Implementation Options +# +# CONFIG_ARCH_IOP310 is not set +# CONFIG_ARCH_IOP321 is not set + +# +# IOP3xx Chipset Features +# + +# +# Intel PXA2xx Implementations +# +# CONFIG_ARCH_LUBBOCK is not set +CONFIG_MACH_MAINSTONE=y +# CONFIG_ARCH_PXA_IDP is not set +CONFIG_PXA27x=y +CONFIG_IWMMXT=y + +# +# SA11x0 Implementations +# + +# +# TI OMAP Implementations +# + +# +# OMAP Core Type +# + +# +# OMAP Board Type +# + +# +# OMAP Feature Selections +# + +# +# S3C2410 Implementations +# + +# +# LH7A40X Implementations +# + +# +# Processor Type +# +CONFIG_CPU_32=y +CONFIG_CPU_XSCALE=y +CONFIG_CPU_32v5=y +CONFIG_CPU_ABRT_EV5T=y +CONFIG_CPU_TLB_V4WBI=y +CONFIG_CPU_MINICACHE=y + +# +# Processor Features +# +# CONFIG_ARM_THUMB is not set +CONFIG_XSCALE_PMU=y + +# +# General setup +# +# CONFIG_ZBOOT_ROM is not set +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 + +# +# PCMCIA/CardBus support +# +CONFIG_PCMCIA=y +# CONFIG_PCMCIA_DEBUG is not set +# CONFIG_TCIC is not set +CONFIG_PCMCIA_PXA2XX=y + +# +# At least one math emulation must be selected +# +CONFIG_FPE_NWFPE=y +# CONFIG_FPE_NWFPE_XP is not set +# CONFIG_FPE_FASTFPE is not set +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_AOUT is not set +# CONFIG_BINFMT_MISC is not set + +# +# Generic Driver Options +# +# CONFIG_FW_LOADER is not set +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_PM is not set +# CONFIG_PREEMPT is not set +# CONFIG_ARTHUR is not set +CONFIG_CMDLINE="root=/dev/nfs ip=bootp console=ttyS0,115200 mem=64M" +CONFIG_LEDS=y +CONFIG_LEDS_TIMER=y +CONFIG_LEDS_CPU=y +CONFIG_ALIGNMENT_TRAP=y + +# +# Parallel port support +# +# CONFIG_PARPORT is not set + +# +# Memory Technology Devices (MTD) +# +CONFIG_MTD=y +# CONFIG_MTD_DEBUG is not set +CONFIG_MTD_PARTITIONS=y +# CONFIG_MTD_CONCAT is not set +CONFIG_MTD_REDBOOT_PARTS=y +# CONFIG_MTD_CMDLINE_PARTS is not set +# CONFIG_MTD_AFS_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set + +# +# RAM/ROM/Flash chip drivers +# +CONFIG_MTD_CFI=y +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_GEN_PROBE=y +CONFIG_MTD_CFI_ADV_OPTIONS=y +CONFIG_MTD_CFI_NOSWAP=y +# CONFIG_MTD_CFI_BE_BYTE_SWAP is not set +# CONFIG_MTD_CFI_LE_BYTE_SWAP is not set +CONFIG_MTD_CFI_GEOMETRY=y +# CONFIG_MTD_CFI_B1 is not set +# CONFIG_MTD_CFI_B2 is not set +CONFIG_MTD_CFI_B4=y +# CONFIG_MTD_CFI_B8 is not set +# CONFIG_MTD_CFI_I1 is not set +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +CONFIG_MTD_CFI_INTELEXT=y +# CONFIG_MTD_CFI_AMDSTD is not set +# CONFIG_MTD_CFI_STAA is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set +# CONFIG_MTD_OBSOLETE_CHIPS is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_PHYSMAP is not set +# CONFIG_MTD_ARM_INTEGRATOR is not set +# CONFIG_MTD_EDB7312 is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLKMTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOC2001PLUS is not set + +# +# NAND Flash Device Drivers +# +# CONFIG_MTD_NAND is not set + +# +# Plug and Play support +# + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +# CONFIG_BLK_DEV_LOOP is not set +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_RAM is not set + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set + +# +# Networking support +# +CONFIG_NET=y + +# +# Networking options +# +# CONFIG_PACKET is not set +# CONFIG_NETLINK_DEV is not set +CONFIG_UNIX=y +# CONFIG_NET_KEY is not set +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_PNP=y +# CONFIG_IP_PNP_DHCP is not set +CONFIG_IP_PNP_BOOTP=y +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_IPV6 is not set +# CONFIG_NETFILTER is not set + +# +# SCTP Configuration (EXPERIMENTAL) +# +# CONFIG_IP_SCTP is not set +# CONFIG_ATM is not set +# CONFIG_BRIDGE is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_FASTROUTE is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_HAMRADIO is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +CONFIG_NETDEVICES=y +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +CONFIG_MII=y +CONFIG_SMC91X=y + +# +# Ethernet (1000 Mbit) +# + +# +# Ethernet (10000 Mbit) +# + +# +# Token Ring devices +# + +# +# Wireless LAN (non-hamradio) +# +# CONFIG_NET_RADIO is not set + +# +# PCMCIA network device support +# +# CONFIG_NET_PCMCIA is not set + +# +# Wan interfaces +# +# CONFIG_WAN is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set +# CONFIG_SHAPER is not set +# CONFIG_NETCONSOLE is not set + +# +# ATA/ATAPI/MFM/RLL support +# +CONFIG_IDE=y +CONFIG_BLK_DEV_IDE=y + +# +# Please see Documentation/ide.txt for help/info on IDE drives +# +CONFIG_BLK_DEV_IDEDISK=y +# CONFIG_IDEDISK_MULTI_MODE is not set +# CONFIG_IDEDISK_STROKE is not set +CONFIG_BLK_DEV_IDECS=y +# CONFIG_BLK_DEV_IDECD is not set +# CONFIG_BLK_DEV_IDETAPE is not set +# CONFIG_BLK_DEV_IDEFLOPPY is not set +# CONFIG_IDE_TASK_IOCTL is not set +# CONFIG_IDE_TASKFILE_IO is not set + +# +# IDE chipset support/bugfixes +# +# CONFIG_IDE_GENERIC is not set +# CONFIG_BLK_DEV_IDEDMA is not set +# CONFIG_IDEDMA_AUTO is not set +# CONFIG_BLK_DEV_HD is not set + +# +# SCSI device support +# +# CONFIG_SCSI is not set + +# +# Fusion MPT device support +# + +# +# IEEE 1394 (FireWire) support +# +# CONFIG_IEEE1394 is not set + +# +# I2O device support +# + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Input device support +# +CONFIG_INPUT=y + +# +# Userland interfaces +# +CONFIG_INPUT_MOUSEDEV=y +CONFIG_INPUT_MOUSEDEV_PSAUX=y +CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_TSDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set + +# +# Input I/O drivers +# +# CONFIG_GAMEPORT is not set +CONFIG_SOUND_GAMEPORT=y +CONFIG_SERIO=y +# CONFIG_SERIO_I8042 is not set +# CONFIG_SERIO_SERPORT is not set +# CONFIG_SERIO_CT82C710 is not set + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +CONFIG_KEYBOARD_ATKBD=y +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_XTKBD is not set +# CONFIG_KEYBOARD_NEWTON is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_PXA=y +CONFIG_SERIAL_PXA_CONSOLE=y +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_UNIX98_PTYS=y +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=256 +# CONFIG_QIC02_TAPE is not set + +# +# IPMI +# +# CONFIG_IPMI_HANDLER is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_NVRAM is not set +# CONFIG_RTC is not set +# CONFIG_GEN_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set +# CONFIG_AGP is not set +# CONFIG_DRM is not set + +# +# PCMCIA character devices +# +# CONFIG_SYNCLINK_CS is not set +# CONFIG_RAW_DRIVER is not set + +# +# I2C support +# +# CONFIG_I2C is not set + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# Digital Video Broadcasting Devices +# +# CONFIG_DVB is not set + +# +# File systems +# +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR is not set +# CONFIG_EXT3_FS is not set +# CONFIG_JBD is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +# CONFIG_VFAT_FS is not set +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_SYSFS=y +# CONFIG_DEVFS_FS is not set +# CONFIG_DEVPTS_FS_XATTR is not set +# CONFIG_TMPFS is not set +# CONFIG_HUGETLB_PAGE is not set +CONFIG_RAMFS=y + +# +# Miscellaneous filesystems +# +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_JFFS_FS is not set +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_FS_DEBUG=0 +# CONFIG_JFFS2_FS_NAND is not set +# CONFIG_CRAMFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set + +# +# Network File Systems +# +CONFIG_NFS_FS=y +# CONFIG_NFS_V3 is not set +# CONFIG_NFS_V4 is not set +# CONFIG_NFS_DIRECTIO is not set +# CONFIG_NFSD is not set +CONFIG_ROOT_NFS=y +CONFIG_LOCKD=y +# CONFIG_EXPORTFS is not set +CONFIG_SUNRPC=y +# CONFIG_RPCSEC_GSS_KRB5 is not set +# CONFIG_SMB_FS is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_INTERMEZZO_FS is not set +# CONFIG_AFS_FS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set + +# +# Native Language Support +# +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +# CONFIG_NLS_CODEPAGE_437 is not set +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +# CONFIG_NLS_UTF8 is not set + +# +# Profiling support +# +# CONFIG_PROFILING is not set + +# +# Graphics support +# +# CONFIG_FB is not set + +# +# Console display driver support +# +# CONFIG_VGA_CONSOLE is not set +# CONFIG_MDA_CONSOLE is not set +CONFIG_DUMMY_CONSOLE=y + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# Misc devices +# + +# +# USB support +# + +# +# USB Gadget Support +# +# CONFIG_USB_GADGET is not set + +# +# Kernel hacking +# +CONFIG_FRAME_POINTER=y +CONFIG_DEBUG_USER=y +CONFIG_DEBUG_INFO=y +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SLAB is not set +CONFIG_MAGIC_SYSRQ=y +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_WAITQ is not set +CONFIG_DEBUG_BUGVERBOSE=y +CONFIG_DEBUG_ERRORS=y +CONFIG_DEBUG_LL=y +# CONFIG_DEBUG_ICEDCC is not set + +# +# Security options +# +# CONFIG_SECURITY is not set + +# +# Cryptographic options +# +# CONFIG_CRYPTO is not set + +# +# Library routines +# +CONFIG_CRC32=y +# CONFIG_LIBCRC32C is not set +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y diff --git a/arch/arm/configs/smdk2410_defconfig b/arch/arm/configs/smdk2410_defconfig new file mode 100644 index 000000000..a88724f26 --- /dev/null +++ b/arch/arm/configs/smdk2410_defconfig @@ -0,0 +1,667 @@ +# +# Automatically generated make config: don't edit +# +CONFIG_ARM=y +CONFIG_MMU=y +CONFIG_UID16=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y +CONFIG_CLEAN_COMPILE=y +CONFIG_STANDALONE=y +CONFIG_BROKEN_ON_SMP=y + +# +# General setup +# +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +# CONFIG_POSIX_MQUEUE is not set +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_SYSCTL=y +# CONFIG_AUDIT is not set +CONFIG_LOG_BUF_SHIFT=14 +# CONFIG_HOTPLUG is not set +# CONFIG_IKCONFIG is not set +# CONFIG_EMBEDDED is not set +CONFIG_KALLSYMS=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_AS=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +CONFIG_CC_OPTIMIZE_FOR_SIZE=y + +# +# Loadable module support +# +# CONFIG_MODULES is not set + +# +# System Type +# +# CONFIG_ARCH_ADIFCC is not set +# CONFIG_ARCH_CLPS7500 is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_CO285 is not set +# CONFIG_ARCH_PXA is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_CAMELOT is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_IOP3XX is not set +# CONFIG_ARCH_L7200 is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_SHARK is not set +CONFIG_ARCH_S3C2410=y +# CONFIG_ARCH_OMAP is not set +# CONFIG_ARCH_LH7A40X is not set +# CONFIG_ARCH_VERSATILE_PB is not set + +# +# CLPS711X/EP721X Implementations +# + +# +# Epxa10db +# + +# +# Footbridge Implementations +# + +# +# IOP3xx Implementation Options +# +# CONFIG_ARCH_IOP310 is not set +# CONFIG_ARCH_IOP321 is not set + +# +# IOP3xx Chipset Features +# + +# +# Intel PXA250/210 Implementations +# + +# +# SA11x0 Implementations +# + +# +# TI OMAP Implementations +# + +# +# OMAP Core Type +# + +# +# OMAP Board Type +# + +# +# OMAP Feature Selections +# + +# +# S3C2410 Implementations +# +# CONFIG_ARCH_BAST is not set +# CONFIG_ARCH_H1940 is not set +CONFIG_ARCH_SMDK2410=y +# CONFIG_MACH_VR1000 is not set + +# +# LH7A40X Implementations +# + +# +# Processor Type +# +CONFIG_CPU_32=y +CONFIG_CPU_ARM920T=y +CONFIG_CPU_32v4=y +CONFIG_CPU_ABRT_EV4T=y +CONFIG_CPU_CACHE_V4WT=y +CONFIG_CPU_COPY_V4WB=y +CONFIG_CPU_TLB_V4WBI=y + +# +# Processor Features +# +CONFIG_ARM_THUMB=y +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_WRITETHROUGH is not set + +# +# General setup +# +# CONFIG_ZBOOT_ROM is not set +CONFIG_ZBOOT_ROM_TEXT=0 +CONFIG_ZBOOT_ROM_BSS=0 + +# +# At least one math emulation must be selected +# +# CONFIG_FPE_NWFPE is not set +# CONFIG_FPE_FASTFPE is not set +CONFIG_BINFMT_ELF=y +CONFIG_BINFMT_AOUT=y +# CONFIG_BINFMT_MISC is not set + +# +# Generic Driver Options +# +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_PM is not set +# CONFIG_PREEMPT is not set +# CONFIG_ARTHUR is not set +CONFIG_CMDLINE="root=1f04 mem=32M" +CONFIG_ALIGNMENT_TRAP=y + +# +# Parallel port support +# +# CONFIG_PARPORT is not set + +# +# Memory Technology Devices (MTD) +# +CONFIG_MTD=y +# CONFIG_MTD_DEBUG is not set +# CONFIG_MTD_PARTITIONS is not set +# CONFIG_MTD_CONCAT is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set + +# +# RAM/ROM/Flash chip drivers +# +CONFIG_MTD_CFI=y +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_GEN_PROBE=y +# CONFIG_MTD_CFI_ADV_OPTIONS is not set +CONFIG_MTD_CFI_INTELEXT=y +# CONFIG_MTD_CFI_AMDSTD is not set +# CONFIG_MTD_CFI_STAA is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set +# CONFIG_MTD_OBSOLETE_CHIPS is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_PHYSMAP is not set +# CONFIG_MTD_ARM_INTEGRATOR is not set +# CONFIG_MTD_EDB7312 is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLKMTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOC2001PLUS is not set + +# +# NAND Flash Device Drivers +# +# CONFIG_MTD_NAND is not set + +# +# Plug and Play support +# + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +# CONFIG_BLK_DEV_LOOP is not set +# CONFIG_BLK_DEV_NBD is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=4096 +# CONFIG_BLK_DEV_INITRD is not set + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set + +# +# Networking support +# +CONFIG_NET=y + +# +# Networking options +# +# CONFIG_PACKET is not set +# CONFIG_NETLINK_DEV is not set +CONFIG_UNIX=y +# CONFIG_NET_KEY is not set +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_PNP=y +# CONFIG_IP_PNP_DHCP is not set +CONFIG_IP_PNP_BOOTP=y +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_IPV6 is not set +# CONFIG_NETFILTER is not set + +# +# SCTP Configuration (EXPERIMENTAL) +# +# CONFIG_IP_SCTP is not set +# CONFIG_ATM is not set +# CONFIG_BRIDGE is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_FASTROUTE is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_HAMRADIO is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +CONFIG_NETDEVICES=y +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +# CONFIG_MII is not set + +# +# Ethernet (1000 Mbit) +# + +# +# Ethernet (10000 Mbit) +# + +# +# Token Ring devices +# + +# +# Wireless LAN (non-hamradio) +# +# CONFIG_NET_RADIO is not set + +# +# Wan interfaces +# +# CONFIG_WAN is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set +# CONFIG_SHAPER is not set +# CONFIG_NETCONSOLE is not set + +# +# ATA/ATAPI/MFM/RLL support +# +# CONFIG_IDE is not set + +# +# SCSI device support +# +# CONFIG_SCSI is not set + +# +# Fusion MPT device support +# + +# +# IEEE 1394 (FireWire) support +# +# CONFIG_IEEE1394 is not set + +# +# I2O device support +# + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Input device support +# +CONFIG_INPUT=y + +# +# Userland interfaces +# +CONFIG_INPUT_MOUSEDEV=y +CONFIG_INPUT_MOUSEDEV_PSAUX=y +CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_TSDEV is not set +# CONFIG_INPUT_EVDEV is not set +# CONFIG_INPUT_EVBUG is not set + +# +# Input I/O drivers +# +# CONFIG_GAMEPORT is not set +CONFIG_SOUND_GAMEPORT=y +CONFIG_SERIO=y +# CONFIG_SERIO_I8042 is not set +CONFIG_SERIO_SERPORT=y +# CONFIG_SERIO_CT82C710 is not set + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +CONFIG_KEYBOARD_ATKBD=y +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_XTKBD is not set +# CONFIG_KEYBOARD_NEWTON is not set +CONFIG_INPUT_MOUSE=y +CONFIG_MOUSE_PS2=y +# CONFIG_MOUSE_SERIAL is not set +# CONFIG_MOUSE_VSXXXAA is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_S3C2410=y +CONFIG_SERIAL_S3C2410_CONSOLE=y +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_UNIX98_PTYS=y +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=256 +# CONFIG_QIC02_TAPE is not set + +# +# IPMI +# +# CONFIG_IPMI_HANDLER is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_NVRAM is not set +# CONFIG_RTC is not set +# CONFIG_GEN_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set +# CONFIG_AGP is not set +# CONFIG_DRM is not set +# CONFIG_RAW_DRIVER is not set + +# +# I2C support +# +# CONFIG_I2C is not set + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# Digital Video Broadcasting Devices +# +# CONFIG_DVB is not set + +# +# File systems +# +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR is not set +# CONFIG_EXT3_FS is not set +# CONFIG_JBD is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_MINIX_FS is not set +CONFIG_ROMFS_FS=y +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +# CONFIG_FAT_FS is not set +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_SYSFS=y +# CONFIG_DEVFS_FS is not set +# CONFIG_DEVPTS_FS_XATTR is not set +# CONFIG_TMPFS is not set +# CONFIG_HUGETLB_PAGE is not set +CONFIG_RAMFS=y + +# +# Miscellaneous filesystems +# +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_JFFS_FS is not set +# CONFIG_JFFS2_FS is not set +# CONFIG_CRAMFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set + +# +# Network File Systems +# +CONFIG_NFS_FS=y +# CONFIG_NFS_V3 is not set +# CONFIG_NFS_V4 is not set +# CONFIG_NFS_DIRECTIO is not set +# CONFIG_NFSD is not set +CONFIG_ROOT_NFS=y +CONFIG_LOCKD=y +# CONFIG_EXPORTFS is not set +CONFIG_SUNRPC=y +# CONFIG_RPCSEC_GSS_KRB5 is not set +# CONFIG_SMB_FS is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_INTERMEZZO_FS is not set +# CONFIG_AFS_FS is not set + +# +# Partition Types +# +CONFIG_PARTITION_ADVANCED=y +# CONFIG_ACORN_PARTITION is not set +# CONFIG_OSF_PARTITION is not set +# CONFIG_AMIGA_PARTITION is not set +# CONFIG_ATARI_PARTITION is not set +# CONFIG_MAC_PARTITION is not set +# CONFIG_MSDOS_PARTITION is not set +# CONFIG_LDM_PARTITION is not set +# CONFIG_NEC98_PARTITION is not set +# CONFIG_SGI_PARTITION is not set +# CONFIG_ULTRIX_PARTITION is not set +# CONFIG_SUN_PARTITION is not set +# CONFIG_EFI_PARTITION is not set + +# +# Native Language Support +# +# CONFIG_NLS is not set + +# +# Profiling support +# +# CONFIG_PROFILING is not set + +# +# Graphics support +# +CONFIG_FB=y +CONFIG_FB_VIRTUAL=y + +# +# Console display driver support +# +# CONFIG_VGA_CONSOLE is not set +# CONFIG_MDA_CONSOLE is not set +CONFIG_DUMMY_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE=y +CONFIG_PCI_CONSOLE=y +# CONFIG_FONTS is not set +CONFIG_FONT_8x8=y +CONFIG_FONT_8x16=y + +# +# Logo configuration +# +# CONFIG_LOGO is not set + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# Misc devices +# + +# +# USB support +# + +# +# USB Gadget Support +# +# CONFIG_USB_GADGET is not set + +# +# Kernel hacking +# +CONFIG_FRAME_POINTER=y +CONFIG_DEBUG_USER=y +# CONFIG_DEBUG_INFO is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SLAB is not set +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_WAITQ is not set +# CONFIG_DEBUG_BUGVERBOSE is not set +# CONFIG_DEBUG_ERRORS is not set +CONFIG_DEBUG_LL=y +# CONFIG_DEBUG_ICEDCC is not set +CONFIG_DEBUG_LL_PRINTK=y +CONFIG_DEBUG_S3C2410_PORT=y +CONFIG_DEBUG_S3C2410_UART=0 + +# +# Security options +# +# CONFIG_SECURITY is not set + +# +# Cryptographic options +# +# CONFIG_CRYPTO is not set + +# +# Library routines +# +CONFIG_CRC32=y +CONFIG_LIBCRC32C=y diff --git a/arch/arm/mach-ixp4xx/Makefile b/arch/arm/mach-ixp4xx/Makefile new file mode 100644 index 000000000..f656397f8 --- /dev/null +++ b/arch/arm/mach-ixp4xx/Makefile @@ -0,0 +1,10 @@ +# +# Makefile for the linux kernel. +# + +obj-y += common.o common-pci.o + +obj-$(CONFIG_ARCH_IXDP4XX) += ixdp425-pci.o ixdp425-setup.o +obj-$(CONFIG_ARCH_ADI_COYOTE) += coyote-pci.o coyote-setup.o +obj-$(CONFIG_ARCH_PRPMC1100) += prpmc1100-pci.o prpmc1100-setup.o + diff --git a/arch/arm/mach-ixp4xx/common-pci.c b/arch/arm/mach-ixp4xx/common-pci.c new file mode 100644 index 000000000..c20dc3226 --- /dev/null +++ b/arch/arm/mach-ixp4xx/common-pci.c @@ -0,0 +1,543 @@ +/* + * arch/arm/mach-ixp4xx/common-pci.c + * + * IXP4XX PCI routines for all platforms + * + * Maintainer: Deepak Saxena + * + * Copyright (C) 2002 Intel Corporation. + * Copyright (C) 2003 Greg Ungerer + * Copyright (C) 2003-2004 MontaVista Software, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + + +/* + * IXP4xx PCI read function is dependent on whether we are + * running A0 or B0 (AppleGate) silicon. + */ +int (*ixp4xx_pci_read)(u32 addr, u32 cmd, u32* data); + +/* + * Base address for PCI regsiter region + */ +unsigned long ixp4xx_pci_reg_base = 0; + +/* + * PCI cfg an I/O routines are done by programming a + * command/byte enable register, and then read/writing + * the data from a data regsiter. We need to ensure + * these transactions are atomic or we will end up + * with corrupt data on the bus or in a driver. + */ +static spinlock_t ixp4xx_pci_lock = SPIN_LOCK_UNLOCKED; + +/* + * Read from PCI config space + */ +static void crp_read(u32 ad_cbe, u32 *data) +{ + unsigned long flags; + spin_lock_irqsave(&ixp4xx_pci_lock, flags); + *PCI_CRP_AD_CBE = ad_cbe; + *data = *PCI_CRP_RDATA; + spin_unlock_irqrestore(&ixp4xx_pci_lock, flags); +} + +/* + * Write to PCI config space + */ +static void crp_write(u32 ad_cbe, u32 data) +{ + unsigned long flags; + spin_lock_irqsave(&ixp4xx_pci_lock, flags); + *PCI_CRP_AD_CBE = CRP_AD_CBE_WRITE | ad_cbe; + *PCI_CRP_WDATA = data; + spin_unlock_irqrestore(&ixp4xx_pci_lock, flags); +} + +static inline int check_master_abort(void) +{ + /* check Master Abort bit after access */ + unsigned long isr = *PCI_ISR; + + if (isr & PCI_ISR_PFE) { + /* make sure the Master Abort bit is reset */ + *PCI_ISR = PCI_ISR_PFE; + pr_debug("%s failed\n", __FUNCTION__); + return 1; + } + + return 0; +} + +int ixp4xx_pci_read_errata(u32 addr, u32 cmd, u32* data) +{ + unsigned long flags; + int retval = 0; + int i; + + spin_lock_irqsave(&ixp4xx_pci_lock, flags); + + *PCI_NP_AD = addr; + + /* + * PCI workaround - only works if NP PCI space reads have + * no side effects!!! Read 8 times. last one will be good. + */ + for (i = 0; i < 8; i++) { + *PCI_NP_CBE = cmd; + *data = *PCI_NP_RDATA; + *data = *PCI_NP_RDATA; + } + + if(check_master_abort()) + retval = 1; + + spin_unlock_irqrestore(&ixp4xx_pci_lock, flags); + return retval; +} + +int ixp4xx_pci_read_no_errata(u32 addr, u32 cmd, u32* data) +{ + unsigned long flags; + int retval = 0; + + spin_lock_irqsave(&ixp4xx_pci_lock, flags); + + *PCI_NP_AD = addr; + + /* set up and execute the read */ + *PCI_NP_CBE = cmd; + + /* the result of the read is now in NP_RDATA */ + *data = *PCI_NP_RDATA; + + if(check_master_abort()) + retval = 1; + + spin_unlock_irqrestore(&ixp4xx_pci_lock, flags); + return retval; +} + +int ixp4xx_pci_write(u32 addr, u32 cmd, u32 data) +{ + unsigned long flags; + int retval = 0; + + spin_lock_irqsave(&ixp4xx_pci_lock, flags); + + *PCI_NP_AD = addr; + + /* set up the write */ + *PCI_NP_CBE = cmd; + + /* execute the write by writing to NP_WDATA */ + *PCI_NP_WDATA = data; + + if(check_master_abort()) + retval = 1; + + spin_unlock_irqrestore(&ixp4xx_pci_lock, flags); + return retval; +} + +static u32 ixp4xx_config_addr(u8 bus_num, u16 devfn, int where) +{ + u32 addr; + if (!bus_num) { + /* type 0 */ + addr = BIT(32-PCI_SLOT(devfn)) | ((PCI_FUNC(devfn)) << 8) | + (where & ~3); + } else { + /* type 1 */ + addr = (bus_num << 16) | ((PCI_SLOT(devfn)) << 11) | + ((PCI_FUNC(devfn)) << 8) | (where & ~3) | 1; + } + return addr; +} + +/* + * Mask table, bits to mask for quantity of size 1, 2 or 4 bytes. + * 0 and 3 are not valid indexes... + */ +static u32 bytemask[] = { + /*0*/ 0, + /*1*/ 0xff, + /*2*/ 0xffff, + /*3*/ 0, + /*4*/ 0xffffffff, +}; + +static u32 local_byte_lane_enable_bits(u32 n, int size) +{ + if (size == 1) + return (0xf & ~BIT(n)) << CRP_AD_CBE_BESL; + if (size == 2) + return (0xf & ~(BIT(n) | BIT(n+1))) << CRP_AD_CBE_BESL; + if (size == 4) + return 0; + return 0xffffffff; +} + +static int local_read_config(int where, int size, u32 *value) +{ + u32 n, data; + pr_debug("local_read_config from %d size %d\n", where, size); + n = where % 4; + crp_read(where & ~3, &data); + *value = (data >> (8*n)) & bytemask[size]; + pr_debug("local_read_config read %#x\n", *value); + return PCIBIOS_SUCCESSFUL; +} + +static int local_write_config(int where, int size, u32 value) +{ + u32 n, byte_enables, data; + pr_debug("local_write_config %#x to %d size %d\n", value, where, size); + n = where % 4; + byte_enables = local_byte_lane_enable_bits(n, size); + if (byte_enables == 0xffffffff) + return PCIBIOS_BAD_REGISTER_NUMBER; + data = value << (8*n); + crp_write((where & ~3) | byte_enables, data); + return PCIBIOS_SUCCESSFUL; +} + +static u32 byte_lane_enable_bits(u32 n, int size) +{ + if (size == 1) + return (0xf & ~BIT(n)) << 4; + if (size == 2) + return (0xf & ~(BIT(n) | BIT(n+1))) << 4; + if (size == 4) + return 0; + return 0xffffffff; +} + +static int read_config(u8 bus_num, u16 devfn, int where, int size, u32 *value) +{ + u32 n, byte_enables, addr, data; + + pr_debug("read_config from %d size %d dev %d:%d:%d\n", where, size, + bus_num, PCI_SLOT(devfn), PCI_FUNC(devfn)); + + *value = 0xffffffff; + n = where % 4; + byte_enables = byte_lane_enable_bits(n, size); + if (byte_enables == 0xffffffff) + return PCIBIOS_BAD_REGISTER_NUMBER; + + addr = ixp4xx_config_addr(bus_num, devfn, where); + if (ixp4xx_pci_read(addr, byte_enables | NP_CMD_CONFIGREAD, &data)) + return PCIBIOS_DEVICE_NOT_FOUND; + + *value = (data >> (8*n)) & bytemask[size]; + pr_debug("read_config_byte read %#x\n", *value); + return PCIBIOS_SUCCESSFUL; +} + +static int write_config(u8 bus_num, u16 devfn, int where, int size, u32 value) +{ + u32 n, byte_enables, addr, data; + + pr_debug("write_config_byte %#x to %d size %d dev %d:%d:%d\n", value, where, + size, bus_num, PCI_SLOT(devfn), PCI_FUNC(devfn)); + + n = where % 4; + byte_enables = byte_lane_enable_bits(n, size); + if (byte_enables == 0xffffffff) + return PCIBIOS_BAD_REGISTER_NUMBER; + + addr = ixp4xx_config_addr(bus_num, devfn, where); + data = value << (8*n); + if (ixp4xx_pci_write(addr, byte_enables | NP_CMD_CONFIGWRITE, data)) + return PCIBIOS_DEVICE_NOT_FOUND; + + return PCIBIOS_SUCCESSFUL; +} + +/* + * Generalized PCI config access functions. + */ +static int ixp4xx_read_config(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *value) +{ + if (bus->number && !PCI_SLOT(devfn)) + return local_read_config(where, size, value); + return read_config(bus->number, devfn, where, size, value); +} + +static int ixp4xx_write_config(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 value) +{ + if (bus->number && !PCI_SLOT(devfn)) + return local_write_config(where, size, value); + return write_config(bus->number, devfn, where, size, value); +} + +struct pci_ops ixp4xx_ops = { + .read = ixp4xx_read_config, + .write = ixp4xx_write_config, +}; + + +/* + * PCI abort handler + */ +static int abort_handler(unsigned long addr, unsigned int fsr, struct pt_regs *regs) +{ + u32 isr, status; + + isr = *PCI_ISR; + local_read_config(PCI_STATUS, 2, &status); + pr_debug("PCI: abort_handler addr = %#lx, isr = %#x, " + "status = %#x\n", addr, isr, status); + + /* make sure the Master Abort bit is reset */ + *PCI_ISR = PCI_ISR_PFE; + status |= PCI_STATUS_REC_MASTER_ABORT; + local_write_config(PCI_STATUS, 2, status); + + /* + * If it was an imprecise abort, then we need to correct the + * return address to be _after_ the instruction. + */ + if (fsr & (1 << 10)) + regs->ARM_pc += 4; + + return 0; +} + + +/* + * Setup DMA mask to 64MB on PCI devices. Ignore all other devices. + */ +static int ixp4xx_pci_platform_notify(struct device *dev) +{ + if(dev->bus == &pci_bus_type) { + *dev->dma_mask = SZ_64M - 1; + dev->coherent_dma_mask = SZ_64M - 1; + dmabounce_register_dev(dev, 2048, 4096); + } + return 0; +} + +static int ixp4xx_pci_platform_notify_remove(struct device *dev) +{ + if(dev->bus == &pci_bus_type) { + dmabounce_unregister_dev(dev); + } + return 0; +} + +int dma_needs_bounce(struct device *dev, dma_addr_t dma_addr, size_t size) +{ + return (dev->bus == &pci_bus_type ) && ((dma_addr + size) >= SZ_64M); +} + +void __init ixp4xx_pci_preinit(void) +{ + unsigned long processor_id; + + asm("mrc p15, 0, %0, cr0, cr0, 0;" : "=r"(processor_id) :); + + /* + * Determine which PCI read method to use + */ + if (!(processor_id & 0xf)) { + printk("PCI: IXP4xx A0 silicon detected - " + "PCI Non-Prefetch Workaround Enabled\n"); + ixp4xx_pci_read = ixp4xx_pci_read_errata; + } else + ixp4xx_pci_read = ixp4xx_pci_read_no_errata; + + + /* hook in our fault handler for PCI errors */ + hook_fault_code(16+6, abort_handler, SIGBUS, "imprecise external abort"); + + pr_debug("setup PCI-AHB(inbound) and AHB-PCI(outbound) address mappings\n"); + + /* + * We use identity AHB->PCI address translation + * in the 0x48000000 to 0x4bffffff address space + */ + *PCI_PCIMEMBASE = 0x48494A4B; + + /* + * We also use identity PCI->AHB address translation + * in 4 16MB BARs that begin at the physical memory start + */ + *PCI_AHBMEMBASE = (PHYS_OFFSET & 0xFF000000) + + ((PHYS_OFFSET & 0xFF000000) >> 8) + + ((PHYS_OFFSET & 0xFF000000) >> 16) + + ((PHYS_OFFSET & 0xFF000000) >> 24) + + 0x00010203; + + if (*PCI_CSR & PCI_CSR_HOST) { + printk("PCI: IXP4xx is host\n"); + + pr_debug("setup BARs in controller\n"); + + /* + * We configure the PCI inbound memory windows to be + * 1:1 mapped to SDRAM + */ + local_write_config(PCI_BASE_ADDRESS_0, 4, PHYS_OFFSET + 0x00000000); + local_write_config(PCI_BASE_ADDRESS_1, 4, PHYS_OFFSET + 0x01000000); + local_write_config(PCI_BASE_ADDRESS_2, 4, PHYS_OFFSET + 0x02000000); + local_write_config(PCI_BASE_ADDRESS_3, 4, PHYS_OFFSET + 0x03000000); + + /* + * Enable CSR window at 0xff000000. + */ + local_write_config(PCI_BASE_ADDRESS_4, 4, 0xff000008); + + /* + * Enable the IO window to be way up high, at 0xfffffc00 + */ + local_write_config(PCI_BASE_ADDRESS_5, 4, 0xfffffc01); + } else { + printk("PCI: IXP4xx is target - No bus scan performed\n"); + } + + printk("PCI: IXP4xx Using %s access for memory space\n", +#ifndef CONFIG_IXP4XX_INDIRECT_PCI + "direct" +#else + "indirect" +#endif + ); + + pr_debug("clear error bits in ISR\n"); + *PCI_ISR = PCI_ISR_PSE | PCI_ISR_PFE | PCI_ISR_PPE | PCI_ISR_AHBE; + + /* + * Set Initialize Complete in PCI Control Register: allow IXP4XX to + * respond to PCI configuration cycles. Specify that the AHB bus is + * operating in big endian mode. Set up byte lane swapping between + * little-endian PCI and the big-endian AHB bus + */ +#ifdef __ARMEB__ + *PCI_CSR = PCI_CSR_IC | PCI_CSR_ABE | PCI_CSR_PDS | PCI_CSR_ADS; +#else + *PCI_CSR = PCI_CSR_IC; +#endif + + pr_debug("DONE\n"); +} + +int ixp4xx_setup(int nr, struct pci_sys_data *sys) +{ + struct resource *res; + + if (nr >= 1) + return 0; + + res = kmalloc(sizeof(*res) * 2, GFP_KERNEL); + if (res == NULL) { + /* + * If we're out of memory this early, something is wrong, + * so we might as well catch it here. + */ + panic("PCI: unable to allocate resources?\n"); + } + memset(res, 0, sizeof(*res) * 2); + + local_write_config(PCI_COMMAND, 2, PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY); + + res[0].name = "PCI I/O Space"; + res[0].start = 0x00001000; + res[0].end = 0xffff0000; + res[0].flags = IORESOURCE_IO; + + res[1].name = "PCI Memory Space"; + res[1].start = 0x48000000; +#ifndef CONFIG_IXP4XX_INDIRECT_PCI + res[1].end = 0x4bffffff; +#else + res[1].end = 0x4fffffff; +#endif + res[1].flags = IORESOURCE_MEM; + + request_resource(&ioport_resource, &res[0]); + request_resource(&iomem_resource, &res[1]); + + sys->resource[0] = &res[0]; + sys->resource[1] = &res[1]; + sys->resource[2] = NULL; + + platform_notify = ixp4xx_pci_platform_notify; + platform_notify_remove = ixp4xx_pci_platform_notify_remove; + + return 1; +} + +struct pci_bus *ixp4xx_scan_bus(int nr, struct pci_sys_data *sys) +{ + return pci_scan_bus(sys->busnr, &ixp4xx_ops, sys); +} + +/* + * We override these so we properly do dmabounce otherwise drivers + * are able to set the dma_mask to 0xffffffff and we can no longer + * trap bounces. :( + * + * We just return true on everyhing except for < 64MB in which case + * we will fail miseralby and die since we can't handle that case. + */ +int +pci_set_dma_mask(struct pci_dev *dev, u64 mask) +{ + if (mask >= SZ_64M - 1 ) + return 0; + + return -EIO; +} + +int +pci_dac_set_dma_mask(struct pci_dev *dev, u64 mask) +{ + if (mask >= SZ_64M - 1 ) + return 0; + + return -EIO; +} + +int +pci_set_consistent_dma_mask(struct pci_dev *dev, u64 mask) +{ + if (mask >= SZ_64M - 1 ) + return 0; + + return -EIO; +} + +EXPORT_SYMBOL(pci_set_dma_mask); +EXPORT_SYMBOL(pci_dac_set_dma_mask); +EXPORT_SYMBOL(pci_set_consistent_dma_mask); + diff --git a/arch/arm/mach-ixp4xx/common.c b/arch/arm/mach-ixp4xx/common.c new file mode 100644 index 000000000..f0166508d --- /dev/null +++ b/arch/arm/mach-ixp4xx/common.c @@ -0,0 +1,263 @@ +/* + * arch/arm/mach-ixp4xx/common.c + * + * Generic code shared across all IXP4XX platforms + * + * Maintainer: Deepak Saxena + * + * Copyright 2002 (c) Intel Corporation + * Copyright 2003-2004 (c) MontaVista, Software, Inc. + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + + +/************************************************************************* + * GPIO acces functions + *************************************************************************/ + +/* + * Configure GPIO line for input, interrupt, or output operation + * + * TODO: Enable/disable the irq_desc based on interrupt or output mode. + * TODO: Should these be named ixp4xx_gpio_? + */ +void gpio_line_config(u8 line, u32 style) +{ + u32 enable; + volatile u32 *int_reg; + u32 int_style; + + enable = *IXP4XX_GPIO_GPOER; + + if (style & IXP4XX_GPIO_OUT) { + enable &= ~((1) << line); + } else if (style & IXP4XX_GPIO_IN) { + enable |= ((1) << line); + + switch (style & IXP4XX_GPIO_INTSTYLE_MASK) + { + case (IXP4XX_GPIO_ACTIVE_HIGH): + int_style = IXP4XX_GPIO_STYLE_ACTIVE_HIGH; + break; + case (IXP4XX_GPIO_ACTIVE_LOW): + int_style = IXP4XX_GPIO_STYLE_ACTIVE_LOW; + break; + case (IXP4XX_GPIO_RISING_EDGE): + int_style = IXP4XX_GPIO_STYLE_RISING_EDGE; + break; + case (IXP4XX_GPIO_FALLING_EDGE): + int_style = IXP4XX_GPIO_STYLE_FALLING_EDGE; + break; + case (IXP4XX_GPIO_TRANSITIONAL): + int_style = IXP4XX_GPIO_STYLE_TRANSITIONAL; + break; + default: + int_style = IXP4XX_GPIO_STYLE_ACTIVE_HIGH; + break; + } + + if (line >= 8) { /* pins 8-15 */ + line -= 8; + int_reg = IXP4XX_GPIO_GPIT2R; + } + else { /* pins 0-7 */ + int_reg = IXP4XX_GPIO_GPIT1R; + } + + /* Clear the style for the appropriate pin */ + *int_reg &= ~(IXP4XX_GPIO_STYLE_CLEAR << + (line * IXP4XX_GPIO_STYLE_SIZE)); + + /* Set the new style */ + *int_reg |= (int_style << (line * IXP4XX_GPIO_STYLE_SIZE)); + } + + *IXP4XX_GPIO_GPOER = enable; +} + +EXPORT_SYMBOL(gpio_line_config); + +/************************************************************************* + * IXP4xx chipset I/O mapping + *************************************************************************/ +static struct map_desc ixp4xx_io_desc[] __initdata = { + { /* UART, Interrupt ctrl, GPIO, timers, NPEs, MACs, USB .... */ + .virtual = IXP4XX_PERIPHERAL_BASE_VIRT, + .physical = IXP4XX_PERIPHERAL_BASE_PHYS, + .length = IXP4XX_PERIPHERAL_REGION_SIZE, + .type = MT_DEVICE + }, { /* Expansion Bus Config Registers */ + .virtual = IXP4XX_EXP_CFG_BASE_VIRT, + .physical = IXP4XX_EXP_CFG_BASE_PHYS, + .length = IXP4XX_EXP_CFG_REGION_SIZE, + .type = MT_DEVICE + }, { /* PCI Registers */ + .virtual = IXP4XX_PCI_CFG_BASE_VIRT, + .physical = IXP4XX_PCI_CFG_BASE_PHYS, + .length = IXP4XX_PCI_CFG_REGION_SIZE, + .type = MT_DEVICE + } +}; + +void __init ixp4xx_map_io(void) +{ + iotable_init(ixp4xx_io_desc, ARRAY_SIZE(ixp4xx_io_desc)); +} + + +/************************************************************************* + * IXP4xx chipset IRQ handling + * + * TODO: GPIO IRQs should be marked invalid until the user of the IRQ + * (be it PCI or something else) configures that GPIO line + * as an IRQ. Also, we should use a different chip structure for + * level-based GPIO vs edge-based GPIO. Currently nobody needs this as + * all HW that's publically available uses level IRQs, so we'll + * worry about it if/when we have HW to test. + **************************************************************************/ +static void ixp4xx_irq_mask(unsigned int irq) +{ + *IXP4XX_ICMR &= ~(1 << irq); +} + +static void ixp4xx_irq_mask_ack(unsigned int irq) +{ + ixp4xx_irq_mask(irq); +} + +static void ixp4xx_irq_unmask(unsigned int irq) +{ + static int irq2gpio[NR_IRQS] = { + -1, -1, -1, -1, -1, -1, 0, 1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 2, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, -1, -1, + }; + int line = irq2gpio[irq]; + + /* + * This only works for LEVEL gpio IRQs as per the IXP4xx developer's + * manual. If edge-triggered, need to move it to the mask_ack. + * Nobody seems to be using the edge-triggered mode on the GPIOs. + */ + if (line >= 0) + gpio_line_isr_clear(line); + + *IXP4XX_ICMR |= (1 << irq); +} + +static struct irqchip ixp4xx_irq_chip = { + .ack = ixp4xx_irq_mask_ack, + .mask = ixp4xx_irq_mask, + .unmask = ixp4xx_irq_unmask, +}; + +void __init ixp4xx_init_irq(void) +{ + int i = 0; + + /* Route all sources to IRQ instead of FIQ */ + *IXP4XX_ICLR = 0x0; + + /* Disable all interrupt */ + *IXP4XX_ICMR = 0x0; + + for(i = 0; i < NR_IRQS; i++) + { + set_irq_chip(i, &ixp4xx_irq_chip); + set_irq_handler(i, do_level_IRQ); + set_irq_flags(i, IRQF_VALID); + } +} + + +/************************************************************************* + * IXP4xx timer tick + * We use OS timer1 on the CPU for the timer tick and the timestamp + * counter as a source of real clock ticks to account for missed jiffies. + *************************************************************************/ + +static unsigned volatile last_jiffy_time; + +#define CLOCK_TICKS_PER_USEC (CLOCK_TICK_RATE / USEC_PER_SEC) + +/* IRQs are disabled before entering here from do_gettimeofday() */ +static unsigned long ixp4xx_gettimeoffset(void) +{ + u32 elapsed; + + elapsed = *IXP4XX_OSTS - last_jiffy_time; + + return elapsed / CLOCK_TICKS_PER_USEC; +} + +static irqreturn_t ixp4xx_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + /* Clear Pending Interrupt by writing '1' to it */ + *IXP4XX_OSST = IXP4XX_OSST_TIMER_1_PEND; + + /* + * Catch up with the real idea of time + */ + do { + do_timer(regs); + last_jiffy_time += LATCH; + } while((*IXP4XX_OSTS - last_jiffy_time) > LATCH); + + return IRQ_HANDLED; +} + +extern unsigned long (*gettimeoffset)(void); + +static struct irqaction timer_irq = { + .name = "IXP4xx Timer Tick", + .flags = SA_INTERRUPT +}; + +void __init time_init(void) +{ + gettimeoffset = ixp4xx_gettimeoffset; + timer_irq.handler = ixp4xx_timer_interrupt; + + /* Clear Pending Interrupt by writing '1' to it */ + *IXP4XX_OSST = IXP4XX_OSST_TIMER_1_PEND; + + /* Setup the Timer counter value */ + *IXP4XX_OSRT1 = (LATCH & ~IXP4XX_OST_RELOAD_MASK) | IXP4XX_OST_ENABLE; + + /* Reset time-stamp counter */ + *IXP4XX_OSTS = 0; + last_jiffy_time = 0; + + /* Connect the interrupt handler and enable the interrupt */ + setup_irq(IRQ_IXP4XX_TIMER1, &timer_irq); +} + + diff --git a/arch/arm/mach-ixp4xx/coyote-pci.c b/arch/arm/mach-ixp4xx/coyote-pci.c new file mode 100644 index 000000000..b46c74351 --- /dev/null +++ b/arch/arm/mach-ixp4xx/coyote-pci.c @@ -0,0 +1,69 @@ +/* + * arch/arch/mach-ixp4xx/coyote-pci.c + * + * PCI setup routines for ADI Engineering Coyote platform + * + * Copyright (C) 2002 Jungo Software Technologies. + * Copyright (C) 2003 MontaVista Softwrae, Inc. + * + * Maintainer: Deepak Saxena + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include + +#include +#include +#include + +#include + +extern void ixp4xx_pci_preinit(void); +extern int ixp4xx_setup(int nr, struct pci_sys_data *sys); +extern struct pci_bus *ixp4xx_scan_bus(int nr, struct pci_sys_data *sys); + +void __init coyote_pci_preinit(void) +{ + gpio_line_config(COYOTE_PCI_SLOT0_PIN, + IXP4XX_GPIO_IN | IXP4XX_GPIO_ACTIVE_LOW); + + gpio_line_config(COYOTE_PCI_SLOT1_PIN, + IXP4XX_GPIO_IN | IXP4XX_GPIO_ACTIVE_LOW); + + gpio_line_isr_clear(COYOTE_PCI_SLOT0_PIN); + gpio_line_isr_clear(COYOTE_PCI_SLOT1_PIN); + + ixp4xx_pci_preinit(); +} + +static int __init coyote_map_irq(struct pci_dev *dev, u8 slot, u8 pin) +{ + if (slot == COYOTE_PCI_SLOT0_DEVID) + return IRQ_COYOTE_PCI_SLOT0; + else if (slot == COYOTE_PCI_SLOT1_DEVID) + return IRQ_COYOTE_PCI_SLOT1; + else return -1; +} + +struct hw_pci coyote_pci __initdata = { + .nr_controllers = 1, + .preinit = coyote_pci_preinit, + .swizzle = pci_std_swizzle, + .setup = ixp4xx_setup, + .scan = ixp4xx_scan_bus, + .map_irq = coyote_map_irq, +}; + +int __init coyote_pci_init(void) +{ + if (machine_is_adi_coyote()) + pci_common_init(&coyote_pci); + return 0; +} + +subsys_initcall(coyote_pci_init); diff --git a/arch/arm/mach-ixp4xx/ixdp425-pci.c b/arch/arm/mach-ixp4xx/ixdp425-pci.c new file mode 100644 index 000000000..7baa60c2d --- /dev/null +++ b/arch/arm/mach-ixp4xx/ixdp425-pci.c @@ -0,0 +1,84 @@ +/* + * arch/arm/mach-ixp4xx/ixdp425-pci.c + * + * IXDP425 board-level PCI initialization + * + * Copyright (C) 2002 Intel Corporation. + * Copyright (C) 2003-2004 MontaVista Software, Inc. + * + * Maintainer: Deepak Saxena + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +void __init ixdp425_pci_preinit(void) +{ + gpio_line_config(IXDP425_PCI_INTA_PIN, + IXP4XX_GPIO_IN | IXP4XX_GPIO_ACTIVE_LOW); + gpio_line_config(IXDP425_PCI_INTB_PIN, + IXP4XX_GPIO_IN | IXP4XX_GPIO_ACTIVE_LOW); + gpio_line_config(IXDP425_PCI_INTC_PIN, + IXP4XX_GPIO_IN | IXP4XX_GPIO_ACTIVE_LOW); + gpio_line_config(IXDP425_PCI_INTD_PIN, + IXP4XX_GPIO_IN | IXP4XX_GPIO_ACTIVE_LOW); + + gpio_line_isr_clear(IXDP425_PCI_INTA_PIN); + gpio_line_isr_clear(IXDP425_PCI_INTB_PIN); + gpio_line_isr_clear(IXDP425_PCI_INTC_PIN); + gpio_line_isr_clear(IXDP425_PCI_INTD_PIN); + + ixp4xx_pci_preinit(); +} + +static int __init ixdp425_map_irq(struct pci_dev *dev, u8 slot, u8 pin) +{ + static int pci_irq_table[IXDP425_PCI_IRQ_LINES] = { + IRQ_IXDP425_PCI_INTA, + IRQ_IXDP425_PCI_INTB, + IRQ_IXDP425_PCI_INTC, + IRQ_IXDP425_PCI_INTD + }; + + int irq = -1; + + if (slot >= 1 && slot <= IXDP425_PCI_MAX_DEV && + pin >= 1 && pin <= IXDP425_PCI_IRQ_LINES) { + irq = pci_irq_table[(slot + pin - 2) % 4]; + } + + return irq; +} + +struct hw_pci ixdp425_pci __initdata = { + .nr_controllers = 1, + .preinit = ixdp425_pci_preinit, + .swizzle = pci_std_swizzle, + .setup = ixp4xx_setup, + .scan = ixp4xx_scan_bus, + .map_irq = ixdp425_map_irq, +}; + +int __init ixdp425_pci_init(void) +{ + if (machine_is_ixdp425() || + machine_is_ixcdp1100() || + machine_is_avila()) + pci_common_init(&ixdp425_pci); + return 0; +} + +subsys_initcall(ixdp425_pci_init); + diff --git a/arch/arm/mach-ixp4xx/prpmc1100-pci.c b/arch/arm/mach-ixp4xx/prpmc1100-pci.c new file mode 100644 index 000000000..a0aed9ca3 --- /dev/null +++ b/arch/arm/mach-ixp4xx/prpmc1100-pci.c @@ -0,0 +1,119 @@ +/* + * arch/arm/mach-ixp4xx/prpmc1100-pci.c + * + * PrPMC1100 PCI initialization + * + * Copyright (C) 2003-2004 MontaVista Sofwtare, Inc. + * Based on IXDP425 code originally (C) Intel Corporation + * + * Author: Deepak Saxena + * + * PrPMC1100 PCI init code. GPIO usage is similar to that on + * IXDP425, but the IRQ routing is completely different and + * depends on what carrier you are using. This code is written + * to work on the Motorola PrPMC800 ATX carrier board. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include + +#include +#include +#include + +#include + + +void __init prpmc1100_pci_preinit(void) +{ + gpio_line_config(PRPMC1100_PCI_INTA_PIN, + IXP4XX_GPIO_IN | IXP4XX_GPIO_ACTIVE_LOW); + gpio_line_config(PRPMC1100_PCI_INTB_PIN, + IXP4XX_GPIO_IN | IXP4XX_GPIO_ACTIVE_LOW); + gpio_line_config(PRPMC1100_PCI_INTC_PIN, + IXP4XX_GPIO_IN | IXP4XX_GPIO_ACTIVE_LOW); + gpio_line_config(PRPMC1100_PCI_INTD_PIN, + IXP4XX_GPIO_IN | IXP4XX_GPIO_ACTIVE_LOW); + + gpio_line_isr_clear(PRPMC1100_PCI_INTA_PIN); + gpio_line_isr_clear(PRPMC1100_PCI_INTB_PIN); + gpio_line_isr_clear(PRPMC1100_PCI_INTC_PIN); + gpio_line_isr_clear(PRPMC1100_PCI_INTD_PIN); + + ixp4xx_pci_preinit(); +} + + +static int __init prpmc1100_map_irq(struct pci_dev *dev, u8 slot, u8 pin) +{ + int irq = -1; + + static int pci_irq_table[][4] = { + { /* IDSEL 16 - PMC A1 */ + IRQ_PRPMC1100_PCI_INTD, + IRQ_PRPMC1100_PCI_INTA, + IRQ_PRPMC1100_PCI_INTB, + IRQ_PRPMC1100_PCI_INTC + }, { /* IDSEL 17 - PRPMC-A-B */ + IRQ_PRPMC1100_PCI_INTD, + IRQ_PRPMC1100_PCI_INTA, + IRQ_PRPMC1100_PCI_INTB, + IRQ_PRPMC1100_PCI_INTC + }, { /* IDSEL 18 - PMC A1-B */ + IRQ_PRPMC1100_PCI_INTA, + IRQ_PRPMC1100_PCI_INTB, + IRQ_PRPMC1100_PCI_INTC, + IRQ_PRPMC1100_PCI_INTD + }, { /* IDSEL 19 - Unused */ + 0, 0, 0, 0 + }, { /* IDSEL 20 - P2P Bridge */ + IRQ_PRPMC1100_PCI_INTA, + IRQ_PRPMC1100_PCI_INTB, + IRQ_PRPMC1100_PCI_INTC, + IRQ_PRPMC1100_PCI_INTD + }, { /* IDSEL 21 - PMC A2 */ + IRQ_PRPMC1100_PCI_INTC, + IRQ_PRPMC1100_PCI_INTD, + IRQ_PRPMC1100_PCI_INTA, + IRQ_PRPMC1100_PCI_INTB + }, { /* IDSEL 22 - PMC A2-B */ + IRQ_PRPMC1100_PCI_INTD, + IRQ_PRPMC1100_PCI_INTA, + IRQ_PRPMC1100_PCI_INTB, + IRQ_PRPMC1100_PCI_INTC + }, + }; + + if (slot >= PRPMC1100_PCI_MIN_DEVID && slot <= PRPMC1100_PCI_MAX_DEVID + && pin >= 1 && pin <= PRPMC1100_PCI_IRQ_LINES) { + irq = pci_irq_table[slot - PRPMC1100_PCI_MIN_DEVID][pin - 1]; + } + + return irq; +} + + +struct hw_pci prpmc1100_pci __initdata = { + .nr_controllers = 1, + .preinit = prpmc1100_pci_preinit, + .swizzle = pci_std_swizzle, + .setup = ixp4xx_setup, + .scan = ixp4xx_scan_bus, + .map_irq = prpmc1100_map_irq, +}; + +int __init prpmc1100_pci_init(void) +{ + if (machine_is_prpmc1100()) + pci_common_init(&prpmc1100_pci); + return 0; +} + +subsys_initcall(prpmc1100_pci_init); + diff --git a/arch/arm/mach-ixp4xx/prpmc1100-setup.c b/arch/arm/mach-ixp4xx/prpmc1100-setup.c new file mode 100644 index 000000000..b0603205d --- /dev/null +++ b/arch/arm/mach-ixp4xx/prpmc1100-setup.c @@ -0,0 +1,90 @@ +/* + * arch/arm/mach-ixp4xx/prpmc1100-setup.c + * + * Motorola PrPMC1100 board setup + * + * Copyright (C) 2003-2004 MontaVista Software, Inc. + * + * Author: Deepak Saxena + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __ARMEB__ +#define REG_OFFSET 3 +#else +#define REG_OFFSET 0 +#endif + +/* + * Only one serial port is connected on the PrPMC1100 + */ +static struct uart_port prpmc1100_serial_port = { + .membase = (char*)(IXP4XX_UART1_BASE_VIRT + REG_OFFSET), + .mapbase = (IXP4XX_UART1_BASE_PHYS), + .irq = IRQ_IXP4XX_UART1, + .flags = UPF_SKIP_TEST, + .iotype = UPIO_MEM, + .regshift = 2, + .uartclk = IXP4XX_UART_XTAL, + .line = 0, + .type = PORT_XSCALE, + .fifosize = 32 +}; + +void __init prpmc1100_map_io(void) +{ + early_serial_setup(&prpmc1100_serial_port); + + ixp4xx_map_io(); +} + +static struct flash_platform_data prpmc1100_flash_data = { + .map_name = "cfi_probe", + .width = 2, +}; + +static struct resource prpmc1100_flash_resource = { + .start = PRPMC1100_FLASH_BASE, + .end = PRPMC1100_FLASH_BASE + PRPMC1100_FLASH_SIZE, + .flags = IORESOURCE_MEM, +}; + +static struct platform_device prpmc1100_flash_device = { + .name = "IXP4XX-Flash", + .id = 0, + .dev = { + .platform_data = &prpmc1100_flash_data, + }, + .num_resources = 1, + .resource = &prpmc1100_flash_resource, +}; + +static void __init prpmc1100_init(void) +{ + platform_add_device(&prpmc1100_flash_device); +} + +MACHINE_START(PRPMC1100, "Motorola PrPMC1100") + MAINTAINER("MontaVista Software, Inc.") + BOOT_MEM(PHYS_OFFSET, IXP4XX_PERIPHERAL_BASE_PHYS, + IXP4XX_PERIPHERAL_BASE_VIRT) + MAPIO(prpmc1100_map_io) + INITIRQ(ixp4xx_init_irq) + BOOT_PARAMS(0x0100) + INIT_MACHINE(prpmc1100_init) +MACHINE_END + diff --git a/arch/arm/mach-omap/innovator1510.c b/arch/arm/mach-omap/innovator1510.c deleted file mode 100644 index 1309f9664..000000000 --- a/arch/arm/mach-omap/innovator1510.c +++ /dev/null @@ -1,99 +0,0 @@ -/* - * linux/arch/arm/mach-omap/innovator1510.c - * - * Board specific inits for OMAP-1510 Innovator - * - * Copyright (C) 2001 RidgeRun, Inc. - * Author: Greg Lonnon - * - * Copyright (C) 2002 MontaVista Software, Inc. - * - * Separated FPGA interrupts from innovator1510.c and cleaned up for 2.6 - * Copyright (C) 2004 Nokia Corporation by Tony Lindrgen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include - -#include -#include -#include - -#include -#include -#include - -#include "common.h" - -extern int omap_gpio_init(void); - -void innovator_init_irq(void) -{ - omap_init_irq(); - omap_gpio_init(); - fpga_init_irq(); -} - -static struct resource smc91x_resources[] = { - [0] = { - .start = OMAP1510P1_FPGA_ETHR_START, /* Physical */ - .end = OMAP1510P1_FPGA_ETHR_START + 16, - .flags = IORESOURCE_MEM, - }, - [1] = { - .start = INT_ETHER, - .end = INT_ETHER, - .flags = IORESOURCE_IRQ, - }, -}; - -static struct platform_device smc91x_device = { - .name = "smc91x", - .id = 0, - .num_resources = ARRAY_SIZE(smc91x_resources), - .resource = smc91x_resources, -}; - -static struct platform_device *devices[] __initdata = { - &smc91x_device, -}; - -static void __init innovator_init(void) -{ - if (!machine_is_innovator()) - return; - - (void) platform_add_devices(devices, ARRAY_SIZE(devices)); -} - -/* Only FPGA needs to be mapped here. All others are done with ioremap */ -static struct map_desc innovator_io_desc[] __initdata = { -{ OMAP1510P1_FPGA_BASE, OMAP1510P1_FPGA_START, OMAP1510P1_FPGA_SIZE, - MT_DEVICE }, -}; - -static void __init innovator_map_io(void) -{ - omap_map_io(); - iotable_init(innovator_io_desc, ARRAY_SIZE(innovator_io_desc)); - - /* Dump the Innovator FPGA rev early - useful info for support. */ - printk("Innovator FPGA Rev %d.%d Board Rev %d\n", - fpga_read(OMAP1510P1_FPGA_REV_HIGH), - fpga_read(OMAP1510P1_FPGA_REV_LOW), - fpga_read(OMAP1510P1_FPGA_BOARD_REV)); -} - -MACHINE_START(INNOVATOR, "TI-Innovator/OMAP1510") - MAINTAINER("MontaVista Software, Inc.") - BOOT_MEM(0x10000000, 0xe0000000, 0xe0000000) - BOOT_PARAMS(0x10000100) - MAPIO(innovator_map_io) - INITIRQ(innovator_init_irq) - INIT_MACHINE(innovator_init) -MACHINE_END diff --git a/arch/arm/mach-omap/innovator1610.c b/arch/arm/mach-omap/innovator1610.c deleted file mode 100644 index 4081735b0..000000000 --- a/arch/arm/mach-omap/innovator1610.c +++ /dev/null @@ -1,91 +0,0 @@ -/* - * linux/arch/arm/mach-omap/innovator1610.c - * - * This file contains Innovator-specific code. - * - * Copyright (C) 2002 MontaVista Software, Inc. - * - * Copyright (C) 2001 RidgeRun, Inc. - * Author: Greg Lonnon - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "common.h" - -void -innovator_init_irq(void) -{ - omap_init_irq(); -} - -static struct resource smc91x_resources[] = { - [0] = { - .start = OMAP1610_ETHR_START, /* Physical */ - .end = OMAP1610_ETHR_START + SZ_4K, - .flags = IORESOURCE_MEM, - }, - [1] = { - .start = 0, /* Really GPIO 0 */ - .end = 0, - .flags = IORESOURCE_IRQ, - }, -}; - -static struct platform_device smc91x_device = { - .name = "smc91x", - .id = 0, - .num_resources = ARRAY_SIZE(smc91x_resources), - .resource = smc91x_resources, -}; - -static struct platform_device *devices[] __initdata = { - &smc91x_device, -}; - -static void __init innovator_init(void) -{ - if (!machine_is_innovator()) - return; - - (void) platform_add_devices(devices, ARRAY_SIZE(devices)); -} - -static struct map_desc innovator_io_desc[] __initdata = { -{ OMAP1610_ETHR_BASE, OMAP1610_ETHR_START, OMAP1610_ETHR_SIZE,MT_DEVICE }, -{ OMAP1610_NOR_FLASH_BASE, OMAP1610_NOR_FLASH_START, OMAP1610_NOR_FLASH_SIZE, - MT_DEVICE }, -}; - -static void __init innovator_map_io(void) -{ - omap_map_io(); - iotable_init(innovator_io_desc, ARRAY_SIZE(innovator_io_desc)); -} - -MACHINE_START(INNOVATOR, "TI-Innovator/OMAP1610") - MAINTAINER("MontaVista Software, Inc.") - BOOT_MEM(0x10000000, 0xe0000000, 0xe0000000) - BOOT_PARAMS(0x10000100) - MAPIO(innovator_map_io) - INITIRQ(innovator_init_irq) - INIT_MACHINE(innovator_init) -MACHINE_END - diff --git a/arch/arm/mach-omap/irq.h b/arch/arm/mach-omap/irq.h deleted file mode 100644 index 8e1aa7810..000000000 --- a/arch/arm/mach-omap/irq.h +++ /dev/null @@ -1,172 +0,0 @@ -/* - * linux/arch/arm/mach-omap/irq.h - * - * OMAP specific interrupt bank definitions - * - * Copyright (C) 2004 Nokia Corporation - * Written by Tony Lindgren - * - * 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 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. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#define OMAP_IRQ_TYPE710 1 -#define OMAP_IRQ_TYPE730 2 -#define OMAP_IRQ_TYPE1510 3 -#define OMAP_IRQ_TYPE1610 4 -#define OMAP_IRQ_TYPE1710 5 - -#define MAX_NR_IRQ_BANKS 4 - -#define BANK_NR_IRQS 32 - -struct omap_irq_desc { - unsigned int cpu_type; - unsigned int start_irq; - unsigned long level_map; - unsigned long base_reg; - unsigned long mask_reg; - unsigned long ack_reg; - struct irqchip *handler; -}; - -struct omap_irq_bank { - unsigned int start_irq; - unsigned long level_map; - unsigned long base_reg; - unsigned long mask_reg; - unsigned long ack_reg; - struct irqchip *handler; -}; - -static void omap_offset_ack_irq(unsigned int irq); -static void omap_offset_mask_irq(unsigned int irq); -static void omap_offset_unmask_irq(unsigned int irq); -static void omap_offset_mask_ack_irq(unsigned int irq); - -/* NOTE: These will not work if irq bank offset != 0x100 */ -#define IRQ_TO_BANK(irq) (irq >> 5) -#define IRQ_BIT(irq) (irq & 0x1f) -#define BANK_OFFSET(bank) ((bank - 1) * 0x100) - -static struct irqchip omap_offset_irq = { - .ack = omap_offset_mask_ack_irq, - .mask = omap_offset_mask_irq, - .unmask = omap_offset_unmask_irq, -}; - -/* - * OMAP-730 interrupt banks - */ -static struct omap_irq_desc omap730_bank0_irqs __initdata = { - .cpu_type = OMAP_IRQ_TYPE730, - .start_irq = 0, - .level_map = 0xb3f8e22f, - .base_reg = OMAP_IH1_BASE, - .mask_reg = OMAP_IH1_BASE + IRQ_MIR, - .ack_reg = OMAP_IH1_BASE + IRQ_CONTROL_REG, - .handler = &omap_offset_irq, /* IH2 regs at 0x100 offsets */ -}; - -static struct omap_irq_desc omap730_bank1_irqs __initdata = { - .cpu_type = OMAP_IRQ_TYPE730, - .start_irq = 32, - .level_map = 0xfdb9c1f2, - .base_reg = OMAP_IH2_BASE, - .mask_reg = OMAP_IH2_BASE + IRQ_MIR, - .ack_reg = OMAP_IH2_BASE + IRQ_CONTROL_REG, - .handler = &omap_offset_irq, /* IH2 regs at 0x100 offsets */ -}; - -static struct omap_irq_desc omap730_bank2_irqs __initdata = { - .cpu_type = OMAP_IRQ_TYPE730, - .start_irq = 64, - .level_map = 0x800040f3, - .base_reg = OMAP_IH2_BASE + 0x100, - .mask_reg = OMAP_IH2_BASE + 0x100 + IRQ_MIR, - .ack_reg = OMAP_IH2_BASE + IRQ_CONTROL_REG, /* Not replicated */ - .handler = &omap_offset_irq, /* IH2 regs at 0x100 offsets */ -}; - -/* - * OMAP-1510 interrupt banks - */ -static struct omap_irq_desc omap1510_bank0_irqs __initdata = { - .cpu_type = OMAP_IRQ_TYPE1510, - .start_irq = 0, - .level_map = 0xb3febfff, - .base_reg = OMAP_IH1_BASE, - .mask_reg = OMAP_IH1_BASE + IRQ_MIR, - .ack_reg = OMAP_IH1_BASE + IRQ_CONTROL_REG, - .handler = &omap_offset_irq, /* IH2 regs at 0x100 offsets */ -}; - -static struct omap_irq_desc omap1510_bank1_irqs __initdata = { - .cpu_type = OMAP_IRQ_TYPE1510, - .start_irq = 32, - .level_map = 0xffbfffed, - .base_reg = OMAP_IH2_BASE, - .mask_reg = OMAP_IH2_BASE + IRQ_MIR, - .ack_reg = OMAP_IH2_BASE + IRQ_CONTROL_REG, - .handler = &omap_offset_irq, /* IH2 regs at 0x100 offsets */ -}; - -/* - * OMAP-1610 interrupt banks - */ -static struct omap_irq_desc omap1610_bank0_irqs __initdata = { - .cpu_type = OMAP_IRQ_TYPE1610, - .start_irq = 0, - .level_map = 0xb3fefe8f, - .base_reg = OMAP_IH1_BASE, - .mask_reg = OMAP_IH1_BASE + IRQ_MIR, - .ack_reg = OMAP_IH1_BASE + IRQ_CONTROL_REG, - .handler = &omap_offset_irq, /* IH2 regs at 0x100 offsets */ -}; - -static struct omap_irq_desc omap1610_bank1_irqs __initdata = { - .cpu_type = OMAP_IRQ_TYPE1610, - .start_irq = 32, - .level_map = 0xfffff7ff, - .base_reg = OMAP_IH2_BASE, - .mask_reg = OMAP_IH2_BASE + IRQ_MIR, - .ack_reg = OMAP_IH2_BASE + IRQ_CONTROL_REG, - .handler = &omap_offset_irq, /* IH2 regs at 0x100 offsets */ -}; - -static struct omap_irq_desc omap1610_bank2_irqs __initdata = { - .cpu_type = OMAP_IRQ_TYPE1610, - .start_irq = 64, - .level_map = 0xffffffff, - .base_reg = OMAP_IH2_BASE + 0x100, - .mask_reg = OMAP_IH2_BASE + 0x100 + IRQ_MIR, - .ack_reg = OMAP_IH2_BASE + IRQ_CONTROL_REG, /* Not replicated */ - .handler = &omap_offset_irq, /* IH2 regs at 0x100 offsets */ -}; - -static struct omap_irq_desc omap1610_bank3_irqs __initdata = { - .cpu_type = OMAP_IRQ_TYPE1610, - .start_irq = 96, - .level_map = 0xffffffff, - .base_reg = OMAP_IH2_BASE + 0x200, - .mask_reg = OMAP_IH2_BASE + 0x200 + IRQ_MIR, - .ack_reg = OMAP_IH2_BASE + IRQ_CONTROL_REG, /* Not replicated */ - .handler = &omap_offset_irq, /* IH2 regs at 0x100 offsets */ -}; diff --git a/arch/arm/mach-omap/omap-generic.c b/arch/arm/mach-omap/omap-generic.c deleted file mode 100644 index 982830dcd..000000000 --- a/arch/arm/mach-omap/omap-generic.c +++ /dev/null @@ -1,77 +0,0 @@ -/* - * linux/arch/arm/mach-omap/generic.c - * - * Modified from innovator.c - * - * Code for generic OMAP board. Should work on many OMAP systems where - * the device drivers take care of all the necessary hardware initialization. - * Do not put any board specific code to this file; create a new machine - * type if you need custom low-level initializations. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include - -#include -#include -#include - -#include -#include -#include - -#include "common.h" - -static void __init omap_generic_init_irq(void) -{ - omap_init_irq(); -} - -/* - * Muxes the serial ports on - */ -static void __init omap_early_serial_init(void) -{ - omap_cfg_reg(UART1_TX); - omap_cfg_reg(UART1_RTS); - - omap_cfg_reg(UART2_TX); - omap_cfg_reg(UART2_RTS); - - omap_cfg_reg(UART3_TX); - omap_cfg_reg(UART3_RX); -} - -static void __init omap_generic_init(void) -{ - if (!machine_is_omap_generic()) - return; - - /* - * Make sure the serial ports are muxed on at this point. - * You have to mux them off in device drivers later on - * if not needed. - */ - if (cpu_is_omap1510()) { - omap_early_serial_init(); - } -} - -static void __init omap_generic_map_io(void) -{ - omap_map_io(); -} - -MACHINE_START(OMAP_GENERIC, "Generic OMAP-1510/1610") - MAINTAINER("Tony Lindgren ") - BOOT_MEM(0x10000000, 0xe0000000, 0xe0000000) - BOOT_PARAMS(0x10000100) - MAPIO(omap_generic_map_io) - INITIRQ(omap_generic_init_irq) - INIT_MACHINE(omap_generic_init) -MACHINE_END diff --git a/arch/arm/mach-omap/omap-perseus2.c b/arch/arm/mach-omap/omap-perseus2.c deleted file mode 100644 index ec05093c9..000000000 --- a/arch/arm/mach-omap/omap-perseus2.c +++ /dev/null @@ -1,116 +0,0 @@ -/* - * linux/arch/arm/mach-omap/omap-perseus2.c - * - * Modified from omap-generic.c - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include - -#include -#include -#include - -#include -#include -#include - -#include - -#include "common.h" - -void omap_perseus2_init_irq(void) -{ - omap_init_irq(); -} - -static struct resource smc91x_resources[] = { - [0] = { - .start = OMAP730_FPGA_ETHR_START, /* Physical */ - .end = OMAP730_FPGA_ETHR_START + SZ_4K, - .flags = IORESOURCE_MEM, - }, - [1] = { - .start = 0, - .end = 0, - .flags = INT_ETHER, - }, -}; - -static struct platform_device smc91x_device = { - .name = "smc91x", - .id = 0, - .num_resources = ARRAY_SIZE(smc91x_resources), - .resource = smc91x_resources, -}; - -static struct platform_device *devices[] __initdata = { - &smc91x_device, -}; - -static void __init omap_perseus2_init(void) -{ - if (!machine_is_omap_perseus2()) - return; - - (void) platform_add_devices(devices, ARRAY_SIZE(devices)); -} - -/* Only FPGA needs to be mapped here. All others are done with ioremap */ -static struct map_desc omap_perseus2_io_desc[] __initdata = { - {OMAP730_FPGA_BASE, OMAP730_FPGA_START, OMAP730_FPGA_SIZE, - MT_DEVICE}, -}; - -static void __init omap_perseus2_map_io(void) -{ - omap_map_io(); - iotable_init(omap_perseus2_io_desc, - ARRAY_SIZE(omap_perseus2_io_desc)); - - /* Early, board-dependent init */ - - /* - * Hold GSM Reset until needed - */ - *DSP_M_CTL &= ~1; - - /* - * UARTs -> done automagically by 8250 driver - */ - - /* - * CSx timings, GPIO Mux ... setup - */ - - /* Flash: CS0 timings setup */ - *((volatile __u32 *) OMAP_FLASH_CFG_0) = 0x0000fff3; - *((volatile __u32 *) OMAP_FLASH_ACFG_0) = 0x00000088; - - /* - * Ethernet support trough the debug board - * CS1 timings setup - */ - *((volatile __u32 *) OMAP_FLASH_CFG_1) = 0x0000fff3; - *((volatile __u32 *) OMAP_FLASH_ACFG_1) = 0x00000000; - - /* - * Configure MPU_EXT_NIRQ IO in IO_CONF9 register, - * It is used as the Ethernet controller interrupt - */ - *((volatile __u32 *) PERSEUS2_IO_CONF_9) &= 0x1FFFFFFF; -} - -MACHINE_START(OMAP_PERSEUS2, "OMAP730 Perseus2") - MAINTAINER("Kevin Hilman ") - BOOT_MEM(0x10000000, 0xe0000000, 0xe0000000) - BOOT_PARAMS(0x10000100) - MAPIO(omap_perseus2_map_io) - INITIRQ(omap_perseus2_init_irq) - INIT_MACHINE(omap_perseus2_init) -MACHINE_END diff --git a/arch/arm/mach-s3c2410/mach-smdk2410.c b/arch/arm/mach-s3c2410/mach-smdk2410.c new file mode 100644 index 000000000..4e0282b12 --- /dev/null +++ b/arch/arm/mach-s3c2410/mach-smdk2410.c @@ -0,0 +1,109 @@ +/*********************************************************************** + * + * linux/arch/arm/mach-s3c2410/mach-smdk2410.c + * + * Copyright (C) 2004 by FS Forth-Systeme GmbH + * All rights reserved. + * + * $Id: mach-smdk2410.c,v 1.1 2004/05/11 14:15:38 mpietrek Exp $ + * @Author: Jonas Dietsche + * + * 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 + * + * @History: + * derived from linux/arch/arm/mach-s3c2410/mach-bast.c, written by + * Ben Dooks + ***********************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "s3c2410.h" + + +static struct map_desc smdk2410_iodesc[] __initdata = { + /* nothing here yet */ +}; + +#define UCON S3C2410_UCON_DEFAULT +#define ULCON S3C2410_LCON_CS8 | S3C2410_LCON_PNONE | S3C2410_LCON_STOPB +#define UFCON S3C2410_UFCON_RXTRIG8 | S3C2410_UFCON_FIFOMODE + +/* base baud rate for all our UARTs */ +static unsigned long smdk2410_serial_clock = 24*1000*1000; + +static struct s3c2410_uartcfg smdk2410_uartcfgs[] = { + [0] = { + .hwport = 0, + .flags = 0, + .clock = &smdk2410_serial_clock, + .ucon = UCON, + .ulcon = ULCON, + .ufcon = UFCON, + }, + [1] = { + .hwport = 1, + .flags = 0, + .clock = &smdk2410_serial_clock, + .ucon = UCON, + .ulcon = ULCON, + .ufcon = UFCON, + }, + [2] = { + .hwport = 2, + .flags = 0, + .clock = &smdk2410_serial_clock, + .ucon = UCON, + .ulcon = ULCON, + .ufcon = UFCON, + } +}; + + +void __init smdk2410_map_io(void) +{ + s3c2410_map_io(smdk2410_iodesc, ARRAY_SIZE(smdk2410_iodesc)); + s3c2410_uartcfgs = smdk2410_uartcfgs; +} + +void __init smdk2410_init_irq(void) +{ + s3c2410_init_irq(); +} + +MACHINE_START(SMDK2410, "SMDK2410") /* @TODO: request a new identifier and switch + * to SMDK2410 */ + MAINTAINER("Jonas Dietsche") + BOOT_MEM(S3C2410_SDRAM_PA, S3C2410_PA_UART, S3C2410_VA_UART) + BOOT_PARAMS(S3C2410_SDRAM_PA + 0x100) + MAPIO(smdk2410_map_io) + INITIRQ(smdk2410_init_irq) +MACHINE_END diff --git a/arch/cris/arch-v10/drivers/ide.c b/arch/cris/arch-v10/drivers/ide.c new file mode 100644 index 000000000..335473c45 --- /dev/null +++ b/arch/cris/arch-v10/drivers/ide.c @@ -0,0 +1,945 @@ +/* $Id: ide.c,v 1.1 2004/01/22 08:22:58 starvik Exp $ + * + * Etrax specific IDE functions, like init and PIO-mode setting etc. + * Almost the entire ide.c is used for the rest of the Etrax ATA driver. + * Copyright (c) 2000-2004 Axis Communications AB + * + * Authors: Bjorn Wesen (initial version) + * Mikael Starvik (pio setup stuff, Linux 2.6 port) + */ + +/* Regarding DMA: + * + * There are two forms of DMA - "DMA handshaking" between the interface and the drive, + * and DMA between the memory and the interface. We can ALWAYS use the latter, since it's + * something built-in in the Etrax. However only some drives support the DMA-mode handshaking + * on the ATA-bus. The normal PC driver and Triton interface disables memory-if DMA when the + * device can't do DMA handshaking for some stupid reason. We don't need to do that. + */ + +#undef REALLY_SLOW_IO /* most systems can safely undef this */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* number of Etrax DMA descriptors */ +#define MAX_DMA_DESCRS 64 + +/* number of times to retry busy-flags when reading/writing IDE-registers + * this can't be too high because a hung harddisk might cause the watchdog + * to trigger (sometimes INB and OUTB are called with irq's disabled) + */ + +#define IDE_REGISTER_TIMEOUT 300 + +#ifdef CONFIG_ETRAX_IDE_CSE1_16_RESET +/* address where the memory-mapped IDE reset bit lives, if used */ +static volatile unsigned long *reset_addr; +#endif + +static int e100_read_command = 0; + +#define LOWDB(x) +#define D(x) + +void +etrax100_ide_outw(unsigned short data, ide_ioreg_t reg) { + int timeleft; + LOWDB(printk("ow: data 0x%x, reg 0x%x\n", data, reg)); + + /* note the lack of handling any timeouts. we stop waiting, but we don't + * really notify anybody. + */ + + timeleft = IDE_REGISTER_TIMEOUT; + /* wait for busy flag */ + while(timeleft && (*R_ATA_STATUS_DATA & IO_MASK(R_ATA_STATUS_DATA, busy))) + timeleft--; + + /* + * Fall through at a timeout, so the ongoing command will be + * aborted by the write below, which is expected to be a dummy + * command to the command register. This happens when a faulty + * drive times out on a command. See comment on timeout in + * INB. + */ + if(!timeleft) + printk("ATA timeout reg 0x%lx := 0x%x\n", reg, data); + + *R_ATA_CTRL_DATA = reg | data; /* write data to the drive's register */ + + timeleft = IDE_REGISTER_TIMEOUT; + /* wait for transmitter ready */ + while(timeleft && !(*R_ATA_STATUS_DATA & + IO_MASK(R_ATA_STATUS_DATA, tr_rdy))) + timeleft--; +} + +void +etrax100_ide_outb(unsigned char data, ide_ioreg_t reg) +{ + etrax100_ide_outw(data, reg); +} + +void +etrax100_ide_outbsync(ide_drive_t *drive, u8 addr, unsigned long port) +{ + etrax100_ide_outw(addr, port); +} + +unsigned short +etrax100_ide_inw(ide_ioreg_t reg) { + int status; + int timeleft; + + timeleft = IDE_REGISTER_TIMEOUT; + /* wait for busy flag */ + while(timeleft && (*R_ATA_STATUS_DATA & IO_MASK(R_ATA_STATUS_DATA, busy))) + timeleft--; + + if(!timeleft) { + /* + * If we're asked to read the status register, like for + * example when a command does not complete for an + * extended time, but the ATA interface is stuck in a + * busy state at the *ETRAX* ATA interface level (as has + * happened repeatedly with at least one bad disk), then + * the best thing to do is to pretend that we read + * "busy" in the status register, so the IDE driver will + * time-out, abort the ongoing command and perform a + * reset sequence. Note that the subsequent OUT_BYTE + * call will also timeout on busy, but as long as the + * write is still performed, everything will be fine. + */ + if ((reg & IO_MASK (R_ATA_CTRL_DATA, addr)) + == IO_FIELD (R_ATA_CTRL_DATA, addr, IDE_STATUS_OFFSET)) + return BUSY_STAT; + else + /* For other rare cases we assume 0 is good enough. */ + return 0; + } + + *R_ATA_CTRL_DATA = reg | IO_STATE(R_ATA_CTRL_DATA, rw, read); /* read data */ + + timeleft = IDE_REGISTER_TIMEOUT; + /* wait for available */ + while(timeleft && !((status = *R_ATA_STATUS_DATA) & + IO_MASK(R_ATA_STATUS_DATA, dav))) + timeleft--; + + if(!timeleft) + return 0; + + LOWDB(printk("inb: 0x%x from reg 0x%x\n", status & 0xff, reg)); + + return (unsigned short)status; +} + +unsigned char +etrax100_ide_inb(ide_ioreg_t reg) +{ + return (unsigned char)etrax100_ide_inw(reg); +} + +/* PIO timing (in R_ATA_CONFIG) + * + * _____________________________ + * ADDRESS : ________/ + * + * _______________ + * DIOR : ____________/ \__________ + * + * _______________ + * DATA : XXXXXXXXXXXXXXXX_______________XXXXXXXX + * + * + * DIOR is unbuffered while address and data is buffered. + * This creates two problems: + * 1. The DIOR pulse is to early (because it is unbuffered) + * 2. The rise time of DIOR is long + * + * There are at least three different plausible solutions + * 1. Use a pad capable of larger currents in Etrax + * 2. Use an external buffer + * 3. Make the strobe pulse longer + * + * Some of the strobe timings below are modified to compensate + * for this. This implies a slight performance decrease. + * + * THIS SHOULD NEVER BE CHANGED! + * + * TODO: Is this true for the latest LX boards still ? + */ + +#define ATA_DMA2_STROBE 4 +#define ATA_DMA2_HOLD 0 +#define ATA_DMA1_STROBE 4 +#define ATA_DMA1_HOLD 1 +#define ATA_DMA0_STROBE 12 +#define ATA_DMA0_HOLD 9 +#define ATA_PIO4_SETUP 1 +#define ATA_PIO4_STROBE 5 +#define ATA_PIO4_HOLD 0 +#define ATA_PIO3_SETUP 1 +#define ATA_PIO3_STROBE 5 +#define ATA_PIO3_HOLD 1 +#define ATA_PIO2_SETUP 1 +#define ATA_PIO2_STROBE 6 +#define ATA_PIO2_HOLD 2 +#define ATA_PIO1_SETUP 2 +#define ATA_PIO1_STROBE 11 +#define ATA_PIO1_HOLD 4 +#define ATA_PIO0_SETUP 4 +#define ATA_PIO0_STROBE 19 +#define ATA_PIO0_HOLD 4 + +static int e100_dma_check (ide_drive_t *drive); +static int e100_dma_begin (ide_drive_t *drive); +static int e100_dma_end (ide_drive_t *drive); +static int e100_dma_read (ide_drive_t *drive); +static int e100_dma_write (ide_drive_t *drive); +static void e100_ide_input_data (ide_drive_t *drive, void *, unsigned int); +static void e100_ide_output_data (ide_drive_t *drive, void *, unsigned int); +static void e100_atapi_input_bytes(ide_drive_t *drive, void *, unsigned int); +static void e100_atapi_output_bytes(ide_drive_t *drive, void *, unsigned int); +static int e100_dma_off (ide_drive_t *drive); +static int e100_dma_verbose (ide_drive_t *drive); + + +/* + * good_dma_drives() lists the model names (from "hdparm -i") + * of drives which do not support mword2 DMA but which are + * known to work fine with this interface under Linux. + */ + +const char *good_dma_drives[] = {"Micropolis 2112A", + "CONNER CTMA 4000", + "CONNER CTT8000-A", + NULL}; + +static void tune_e100_ide(ide_drive_t *drive, byte pio) +{ + pio = 4; + /* pio = ide_get_best_pio_mode(drive, pio, 4, NULL); */ + + /* set pio mode! */ + + switch(pio) { + case 0: + *R_ATA_CONFIG = ( IO_FIELD( R_ATA_CONFIG, enable, 1 ) | + IO_FIELD( R_ATA_CONFIG, dma_strobe, ATA_DMA2_STROBE ) | + IO_FIELD( R_ATA_CONFIG, dma_hold, ATA_DMA2_HOLD ) | + IO_FIELD( R_ATA_CONFIG, pio_setup, ATA_PIO0_SETUP ) | + IO_FIELD( R_ATA_CONFIG, pio_strobe, ATA_PIO0_STROBE ) | + IO_FIELD( R_ATA_CONFIG, pio_hold, ATA_PIO0_HOLD ) ); + break; + case 1: + *R_ATA_CONFIG = ( IO_FIELD( R_ATA_CONFIG, enable, 1 ) | + IO_FIELD( R_ATA_CONFIG, dma_strobe, ATA_DMA2_STROBE ) | + IO_FIELD( R_ATA_CONFIG, dma_hold, ATA_DMA2_HOLD ) | + IO_FIELD( R_ATA_CONFIG, pio_setup, ATA_PIO1_SETUP ) | + IO_FIELD( R_ATA_CONFIG, pio_strobe, ATA_PIO1_STROBE ) | + IO_FIELD( R_ATA_CONFIG, pio_hold, ATA_PIO1_HOLD ) ); + break; + case 2: + *R_ATA_CONFIG = ( IO_FIELD( R_ATA_CONFIG, enable, 1 ) | + IO_FIELD( R_ATA_CONFIG, dma_strobe, ATA_DMA2_STROBE ) | + IO_FIELD( R_ATA_CONFIG, dma_hold, ATA_DMA2_HOLD ) | + IO_FIELD( R_ATA_CONFIG, pio_setup, ATA_PIO2_SETUP ) | + IO_FIELD( R_ATA_CONFIG, pio_strobe, ATA_PIO2_STROBE ) | + IO_FIELD( R_ATA_CONFIG, pio_hold, ATA_PIO2_HOLD ) ); + break; + case 3: + *R_ATA_CONFIG = ( IO_FIELD( R_ATA_CONFIG, enable, 1 ) | + IO_FIELD( R_ATA_CONFIG, dma_strobe, ATA_DMA2_STROBE ) | + IO_FIELD( R_ATA_CONFIG, dma_hold, ATA_DMA2_HOLD ) | + IO_FIELD( R_ATA_CONFIG, pio_setup, ATA_PIO3_SETUP ) | + IO_FIELD( R_ATA_CONFIG, pio_strobe, ATA_PIO3_STROBE ) | + IO_FIELD( R_ATA_CONFIG, pio_hold, ATA_PIO3_HOLD ) ); + break; + case 4: + *R_ATA_CONFIG = ( IO_FIELD( R_ATA_CONFIG, enable, 1 ) | + IO_FIELD( R_ATA_CONFIG, dma_strobe, ATA_DMA2_STROBE ) | + IO_FIELD( R_ATA_CONFIG, dma_hold, ATA_DMA2_HOLD ) | + IO_FIELD( R_ATA_CONFIG, pio_setup, ATA_PIO4_SETUP ) | + IO_FIELD( R_ATA_CONFIG, pio_strobe, ATA_PIO4_STROBE ) | + IO_FIELD( R_ATA_CONFIG, pio_hold, ATA_PIO4_HOLD ) ); + break; + } +} + +void __init +init_e100_ide (void) +{ + volatile unsigned int dummy; + int h; + + printk("ide: ETRAX 100LX built-in ATA DMA controller\n"); + + /* first fill in some stuff in the ide_hwifs fields */ + + for(h = 0; h < MAX_HWIFS; h++) { + ide_hwif_t *hwif = &ide_hwifs[h]; + hwif->mmio = 2; + hwif->chipset = ide_etrax100; + hwif->tuneproc = &tune_e100_ide; + hwif->ata_input_data = &e100_ide_input_data; + hwif->ata_output_data = &e100_ide_output_data; + hwif->atapi_input_bytes = &e100_atapi_input_bytes; + hwif->atapi_output_bytes = &e100_atapi_output_bytes; + hwif->ide_dma_check = &e100_dma_check; + hwif->ide_dma_end = &e100_dma_end; + hwif->ide_dma_write = &e100_dma_write; + hwif->ide_dma_read = &e100_dma_read; + hwif->ide_dma_begin = &e100_dma_begin; + hwif->OUTB = &etrax100_ide_outb; + hwif->OUTW = &etrax100_ide_outw; + hwif->OUTBSYNC = &etrax100_ide_outbsync; + hwif->INB = &etrax100_ide_inb; + hwif->INW = &etrax100_ide_inw; + hwif->ide_dma_off_quietly = &e100_dma_off; + hwif->ide_dma_verbose = &e100_dma_verbose; + hwif->sg_table = + kmalloc(sizeof(struct scatterlist) * PRD_ENTRIES, GFP_KERNEL); + } + + /* actually reset and configure the etrax100 ide/ata interface */ + + *R_ATA_CTRL_DATA = 0; + *R_ATA_TRANSFER_CNT = 0; + *R_ATA_CONFIG = 0; + + genconfig_shadow = (genconfig_shadow & + ~IO_MASK(R_GEN_CONFIG, dma2) & + ~IO_MASK(R_GEN_CONFIG, dma3) & + ~IO_MASK(R_GEN_CONFIG, ata)) | + ( IO_STATE( R_GEN_CONFIG, dma3, ata ) | + IO_STATE( R_GEN_CONFIG, dma2, ata ) | + IO_STATE( R_GEN_CONFIG, ata, select ) ); + + *R_GEN_CONFIG = genconfig_shadow; + + /* pull the chosen /reset-line low */ + +#ifdef CONFIG_ETRAX_IDE_G27_RESET + REG_SHADOW_SET(R_PORT_G_DATA, port_g_data_shadow, 27, 0); +#endif +#ifdef CONFIG_ETRAX_IDE_CSE1_16_RESET + REG_SHADOW_SET(port_cse1_addr, port_cse1_shadow, 16, 0); +#endif +#ifdef CONFIG_ETRAX_IDE_CSP0_8_RESET + REG_SHADOW_SET(port_csp0_addr, port_csp0_shadow, 8, 0); +#endif +#ifdef CONFIG_ETRAX_IDE_PB7_RESET + port_pb_dir_shadow = port_pb_dir_shadow | + IO_STATE(R_PORT_PB_DIR, dir7, output); + *R_PORT_PB_DIR = port_pb_dir_shadow; + REG_SHADOW_SET(R_PORT_PB_DATA, port_pb_data_shadow, 7, 1); +#endif + + /* wait some */ + + udelay(25); + + /* de-assert bus-reset */ + +#ifdef CONFIG_ETRAX_IDE_CSE1_16_RESET + REG_SHADOW_SET(port_cse1_addr, port_cse1_shadow, 16, 1); +#endif +#ifdef CONFIG_ETRAX_IDE_CSP0_8_RESET + REG_SHADOW_SET(port_csp0_addr, port_csp0_shadow, 8, 1); +#endif +#ifdef CONFIG_ETRAX_IDE_G27_RESET + REG_SHADOW_SET(R_PORT_G_DATA, port_g_data_shadow, 27, 1); +#endif + + /* make a dummy read to set the ata controller in a proper state */ + dummy = *R_ATA_STATUS_DATA; + + *R_ATA_CONFIG = ( IO_FIELD( R_ATA_CONFIG, enable, 1 ) | + IO_FIELD( R_ATA_CONFIG, dma_strobe, ATA_DMA2_STROBE ) | + IO_FIELD( R_ATA_CONFIG, dma_hold, ATA_DMA2_HOLD ) | + IO_FIELD( R_ATA_CONFIG, pio_setup, ATA_PIO4_SETUP ) | + IO_FIELD( R_ATA_CONFIG, pio_strobe, ATA_PIO4_STROBE ) | + IO_FIELD( R_ATA_CONFIG, pio_hold, ATA_PIO4_HOLD ) ); + + *R_ATA_CTRL_DATA = ( IO_STATE( R_ATA_CTRL_DATA, rw, read) | + IO_FIELD( R_ATA_CTRL_DATA, addr, 1 ) ); + + while(*R_ATA_STATUS_DATA & IO_MASK(R_ATA_STATUS_DATA, busy)); /* wait for busy flag*/ + + *R_IRQ_MASK0_SET = ( IO_STATE( R_IRQ_MASK0_SET, ata_irq0, set ) | + IO_STATE( R_IRQ_MASK0_SET, ata_irq1, set ) | + IO_STATE( R_IRQ_MASK0_SET, ata_irq2, set ) | + IO_STATE( R_IRQ_MASK0_SET, ata_irq3, set ) ); + + printk("ide: waiting %d seconds for drives to regain consciousness\n", + CONFIG_ETRAX_IDE_DELAY); + + h = jiffies + (CONFIG_ETRAX_IDE_DELAY * HZ); + while(time_before(jiffies, h)) /* nothing */ ; + + /* reset the dma channels we will use */ + + RESET_DMA(ATA_TX_DMA_NBR); + RESET_DMA(ATA_RX_DMA_NBR); + WAIT_DMA(ATA_TX_DMA_NBR); + WAIT_DMA(ATA_RX_DMA_NBR); + +} + +static int e100_dma_off (ide_drive_t *drive) +{ + return 0; +} + +static int e100_dma_verbose (ide_drive_t *drive) +{ + printk(", DMA(mode 2)"); + return 0; +} + +static etrax_dma_descr mydescr; + +/* + * The following routines are mainly used by the ATAPI drivers. + * + * These routines will round up any request for an odd number of bytes, + * so if an odd bytecount is specified, be sure that there's at least one + * extra byte allocated for the buffer. + */ +static void +e100_atapi_input_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount) +{ + ide_ioreg_t data_reg = IDE_DATA_REG; + + D(printk("atapi_input_bytes, dreg 0x%x, buffer 0x%x, count %d\n", + data_reg, buffer, bytecount)); + + if(bytecount & 1) { + printk("warning, odd bytecount in cdrom_in_bytes = %d.\n", bytecount); + bytecount++; /* to round off */ + } + + /* make sure the DMA channel is available */ + RESET_DMA(ATA_RX_DMA_NBR); + WAIT_DMA(ATA_RX_DMA_NBR); + + /* setup DMA descriptor */ + + mydescr.sw_len = bytecount; + mydescr.ctrl = d_eol; + mydescr.buf = virt_to_phys(buffer); + + /* start the dma channel */ + + *R_DMA_CH3_FIRST = virt_to_phys(&mydescr); + *R_DMA_CH3_CMD = IO_STATE(R_DMA_CH3_CMD, cmd, start); + + /* initiate a multi word dma read using PIO handshaking */ + + *R_ATA_TRANSFER_CNT = IO_FIELD(R_ATA_TRANSFER_CNT, count, bytecount >> 1); + + *R_ATA_CTRL_DATA = data_reg | + IO_STATE(R_ATA_CTRL_DATA, rw, read) | + IO_STATE(R_ATA_CTRL_DATA, src_dst, dma) | + IO_STATE(R_ATA_CTRL_DATA, handsh, pio) | + IO_STATE(R_ATA_CTRL_DATA, multi, on) | + IO_STATE(R_ATA_CTRL_DATA, dma_size, word); + + /* wait for completion */ + + LED_DISK_READ(1); + WAIT_DMA(ATA_RX_DMA_NBR); + LED_DISK_READ(0); + +#if 0 + /* old polled transfer code + * this should be moved into a new function that can do polled + * transfers if DMA is not available + */ + + /* initiate a multi word read */ + + *R_ATA_TRANSFER_CNT = wcount << 1; + + *R_ATA_CTRL_DATA = data_reg | + IO_STATE(R_ATA_CTRL_DATA, rw, read) | + IO_STATE(R_ATA_CTRL_DATA, src_dst, register) | + IO_STATE(R_ATA_CTRL_DATA, handsh, pio) | + IO_STATE(R_ATA_CTRL_DATA, multi, on) | + IO_STATE(R_ATA_CTRL_DATA, dma_size, word); + + /* svinto has a latency until the busy bit actually is set */ + + nop(); nop(); + nop(); nop(); + nop(); nop(); + nop(); nop(); + nop(); nop(); + + /* unit should be busy during multi transfer */ + while((status = *R_ATA_STATUS_DATA) & IO_MASK(R_ATA_STATUS_DATA, busy)) { + while(!(status & IO_MASK(R_ATA_STATUS_DATA, dav))) + status = *R_ATA_STATUS_DATA; + *ptr++ = (unsigned short)(status & 0xffff); + } +#endif +} + +static void +e100_atapi_output_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount) +{ + ide_ioreg_t data_reg = IDE_DATA_REG; + + D(printk("atapi_output_bytes, dreg 0x%x, buffer 0x%x, count %d\n", + data_reg, buffer, bytecount)); + + if(bytecount & 1) { + printk("odd bytecount %d in atapi_out_bytes!\n", bytecount); + bytecount++; + } + + /* make sure the DMA channel is available */ + RESET_DMA(ATA_TX_DMA_NBR); + WAIT_DMA(ATA_TX_DMA_NBR); + + /* setup DMA descriptor */ + + mydescr.sw_len = bytecount; + mydescr.ctrl = d_eol; + mydescr.buf = virt_to_phys(buffer); + + /* start the dma channel */ + + *R_DMA_CH2_FIRST = virt_to_phys(&mydescr); + *R_DMA_CH2_CMD = IO_STATE(R_DMA_CH2_CMD, cmd, start); + + /* initiate a multi word dma write using PIO handshaking */ + + *R_ATA_TRANSFER_CNT = IO_FIELD(R_ATA_TRANSFER_CNT, count, bytecount >> 1); + + *R_ATA_CTRL_DATA = data_reg | + IO_STATE(R_ATA_CTRL_DATA, rw, write) | + IO_STATE(R_ATA_CTRL_DATA, src_dst, dma) | + IO_STATE(R_ATA_CTRL_DATA, handsh, pio) | + IO_STATE(R_ATA_CTRL_DATA, multi, on) | + IO_STATE(R_ATA_CTRL_DATA, dma_size, word); + + /* wait for completion */ + + LED_DISK_WRITE(1); + WAIT_DMA(ATA_TX_DMA_NBR); + LED_DISK_WRITE(0); + +#if 0 + /* old polled write code - see comment in input_bytes */ + + /* wait for busy flag */ + while(*R_ATA_STATUS_DATA & IO_MASK(R_ATA_STATUS_DATA, busy)); + + /* initiate a multi word write */ + + *R_ATA_TRANSFER_CNT = bytecount >> 1; + + ctrl = data_reg | + IO_STATE(R_ATA_CTRL_DATA, rw, write) | + IO_STATE(R_ATA_CTRL_DATA, src_dst, register) | + IO_STATE(R_ATA_CTRL_DATA, handsh, pio) | + IO_STATE(R_ATA_CTRL_DATA, multi, on) | + IO_STATE(R_ATA_CTRL_DATA, dma_size, word); + + LED_DISK_WRITE(1); + + /* Etrax will set busy = 1 until the multi pio transfer has finished + * and tr_rdy = 1 after each successful word transfer. + * When the last byte has been transferred Etrax will first set tr_tdy = 1 + * and then busy = 0 (not in the same cycle). If we read busy before it + * has been set to 0 we will think that we should transfer more bytes + * and then tr_rdy would be 0 forever. This is solved by checking busy + * in the inner loop. + */ + + do { + *R_ATA_CTRL_DATA = ctrl | *ptr++; + while(!(*R_ATA_STATUS_DATA & IO_MASK(R_ATA_STATUS_DATA, tr_rdy)) && + (*R_ATA_STATUS_DATA & IO_MASK(R_ATA_STATUS_DATA, busy))); + } while(*R_ATA_STATUS_DATA & IO_MASK(R_ATA_STATUS_DATA, busy)); + + LED_DISK_WRITE(0); +#endif + +} + +/* + * This is used for most PIO data transfers *from* the IDE interface + */ +static void +e100_ide_input_data (ide_drive_t *drive, void *buffer, unsigned int wcount) +{ + e100_atapi_input_bytes(drive, buffer, wcount << 2); +} + +/* + * This is used for most PIO data transfers *to* the IDE interface + */ +static void +e100_ide_output_data (ide_drive_t *drive, void *buffer, unsigned int wcount) +{ + e100_atapi_output_bytes(drive, buffer, wcount << 2); +} + +/* we only have one DMA channel on the chip for ATA, so we can keep these statically */ +static etrax_dma_descr ata_descrs[MAX_DMA_DESCRS]; +static unsigned int ata_tot_size; + +/* + * e100_ide_build_dmatable() prepares a dma request. + * Returns 0 if all went okay, returns 1 otherwise. + */ +static int e100_ide_build_dmatable (ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + struct scatterlist* sg; + struct request *rq = HWGROUP(drive)->rq; + unsigned long size, addr; + unsigned int count = 0; + int i = 0; + + sg = hwif->sg_table; + + ata_tot_size = 0; + + if (HWGROUP(drive)->rq->flags & REQ_DRIVE_TASKFILE) { + u8 *virt_addr = rq->buffer; + int sector_count = rq->nr_sectors; + memset(&sg[0], 0, sizeof(*sg)); + sg[0].page = virt_to_page(virt_addr); + sg[0].offset = offset_in_page(virt_addr); + sg[0].length = sector_count * SECTOR_SIZE; + hwif->sg_nents = i = 1; + } + else + { + hwif->sg_nents = i = blk_rq_map_sg(drive->queue, rq, hwif->sg_table); + } + + + while(i) { + /* + * Determine addr and size of next buffer area. We assume that + * individual virtual buffers are always composed linearly in + * physical memory. For example, we assume that any 8kB buffer + * is always composed of two adjacent physical 4kB pages rather + * than two possibly non-adjacent physical 4kB pages. + */ + /* group sequential buffers into one large buffer */ + addr = page_to_phys(sg->page) + sg->offset; + size = sg_dma_len(sg); + while (sg++, --i) { + if ((addr + size) != page_to_phys(sg->page) + sg->offset) + break; + size += sg_dma_len(sg); + } + + /* did we run out of descriptors? */ + + if(count >= MAX_DMA_DESCRS) { + printk("%s: too few DMA descriptors\n", drive->name); + return 1; + } + + /* however, this case is more difficult - R_ATA_TRANSFER_CNT cannot be more + than 65536 words per transfer, so in that case we need to either + 1) use a DMA interrupt to re-trigger R_ATA_TRANSFER_CNT and continue with + the descriptors, or + 2) simply do the request here, and get dma_intr to only ide_end_request on + those blocks that were actually set-up for transfer. + */ + + if(ata_tot_size + size > 131072) { + printk("too large total ATA DMA request, %d + %d!\n", ata_tot_size, (int)size); + return 1; + } + + /* If size > 65536 it has to be splitted into new descriptors. Since we don't handle + size > 131072 only one split is necessary */ + + if(size > 65536) { + /* ok we want to do IO at addr, size bytes. set up a new descriptor entry */ + ata_descrs[count].sw_len = 0; /* 0 means 65536, this is a 16-bit field */ + ata_descrs[count].ctrl = 0; + ata_descrs[count].buf = addr; + ata_descrs[count].next = virt_to_phys(&ata_descrs[count + 1]); + count++; + ata_tot_size += 65536; + /* size and addr should refere to not handled data */ + size -= 65536; + addr += 65536; + } + /* ok we want to do IO at addr, size bytes. set up a new descriptor entry */ + if(size == 65536) { + ata_descrs[count].sw_len = 0; /* 0 means 65536, this is a 16-bit field */ + } else { + ata_descrs[count].sw_len = size; + } + ata_descrs[count].ctrl = 0; + ata_descrs[count].buf = addr; + ata_descrs[count].next = virt_to_phys(&ata_descrs[count + 1]); + count++; + ata_tot_size += size; + } + + if (count) { + /* set the end-of-list flag on the last descriptor */ + ata_descrs[count - 1].ctrl |= d_eol; + /* return and say all is ok */ + return 0; + } + + printk("%s: empty DMA table?\n", drive->name); + return 1; /* let the PIO routines handle this weirdness */ +} + +static int config_drive_for_dma (ide_drive_t *drive) +{ + const char **list; + struct hd_driveid *id = drive->id; + + if (id && (id->capability & 1)) { + /* Enable DMA on any drive that supports mword2 DMA */ + if ((id->field_valid & 2) && (id->dma_mword & 0x404) == 0x404) { + drive->using_dma = 1; + return 0; /* DMA enabled */ + } + + /* Consult the list of known "good" drives */ + list = good_dma_drives; + while (*list) { + if (!strcmp(*list++,id->model)) { + drive->using_dma = 1; + return 0; /* DMA enabled */ + } + } + } + return 1; /* DMA not enabled */ +} + +/* + * etrax_dma_intr() is the handler for disk read/write DMA interrupts + */ +static ide_startstop_t etrax_dma_intr (ide_drive_t *drive) +{ + int i, dma_stat; + byte stat; + + LED_DISK_READ(0); + LED_DISK_WRITE(0); + + dma_stat = HWIF(drive)->ide_dma_end(drive); + stat = HWIF(drive)->INB(IDE_STATUS_REG); /* get drive status */ + if (OK_STAT(stat,DRIVE_READY,drive->bad_wstat|DRQ_STAT)) { + if (!dma_stat) { + struct request *rq; + rq = HWGROUP(drive)->rq; + for (i = rq->nr_sectors; i > 0;) { + i -= rq->current_nr_sectors; + DRIVER(drive)->end_request(drive, 1, rq->nr_sectors); + } + return ide_stopped; + } + printk("%s: bad DMA status\n", drive->name); + } + return DRIVER(drive)->error(drive, "dma_intr", stat); +} + +/* + * Functions below initiates/aborts DMA read/write operations on a drive. + * + * The caller is assumed to have selected the drive and programmed the drive's + * sector address using CHS or LBA. All that remains is to prepare for DMA + * and then issue the actual read/write DMA/PIO command to the drive. + * + * For ATAPI devices, we just prepare for DMA and return. The caller should + * then issue the packet command to the drive and call us again with + * ide_dma_begin afterwards. + * + * Returns 0 if all went well. + * Returns 1 if DMA read/write could not be started, in which case + * the caller should revert to PIO for the current request. + */ + +static int e100_dma_check(ide_drive_t *drive) +{ + return config_drive_for_dma (drive); +} + +static int e100_dma_end(ide_drive_t *drive) +{ + /* TODO: check if something went wrong with the DMA */ + return 0; +} + +static int e100_start_dma(ide_drive_t *drive, int atapi, int reading) +{ + if(reading) { + + RESET_DMA(ATA_RX_DMA_NBR); /* sometimes the DMA channel get stuck so we need to do this */ + WAIT_DMA(ATA_RX_DMA_NBR); + + /* set up the Etrax DMA descriptors */ + + if(e100_ide_build_dmatable (drive)) + return 1; + + if(!atapi) { + /* set the irq handler which will finish the request when DMA is done */ + + ide_set_handler(drive, &etrax_dma_intr, WAIT_CMD, NULL); + + /* issue cmd to drive */ + if ((HWGROUP(drive)->rq->cmd == IDE_DRIVE_TASKFILE) && + (drive->addressing == 1)) { + ide_task_t *args = HWGROUP(drive)->rq->special; + etrax100_ide_outb(args->tfRegister[IDE_COMMAND_OFFSET], IDE_COMMAND_REG); + } else if (drive->addressing) { + etrax100_ide_outb(WIN_READDMA_EXT, IDE_COMMAND_REG); + } else { + etrax100_ide_outb(WIN_READDMA, IDE_COMMAND_REG); + } + } + + /* begin DMA */ + + /* need to do this before RX DMA due to a chip bug + * it is enough to just flush the part of the cache that + * corresponds to the buffers we start, but since HD transfers + * usually are more than 8 kB, it is easier to optimize for the + * normal case and just flush the entire cache. its the only + * way to be sure! (OB movie quote) + */ + flush_etrax_cache(); + *R_DMA_CH3_FIRST = virt_to_phys(ata_descrs); + *R_DMA_CH3_CMD = IO_STATE(R_DMA_CH3_CMD, cmd, start); + + /* initiate a multi word dma read using DMA handshaking */ + + *R_ATA_TRANSFER_CNT = + IO_FIELD(R_ATA_TRANSFER_CNT, count, ata_tot_size >> 1); + + *R_ATA_CTRL_DATA = + IO_FIELD(R_ATA_CTRL_DATA, data, IDE_DATA_REG) | + IO_STATE(R_ATA_CTRL_DATA, rw, read) | + IO_STATE(R_ATA_CTRL_DATA, src_dst, dma) | + IO_STATE(R_ATA_CTRL_DATA, handsh, dma) | + IO_STATE(R_ATA_CTRL_DATA, multi, on) | + IO_STATE(R_ATA_CTRL_DATA, dma_size, word); + + LED_DISK_READ(1); + + D(printk("dma read of %d bytes.\n", ata_tot_size)); + + } else { + /* writing */ + + RESET_DMA(ATA_TX_DMA_NBR); /* sometimes the DMA channel get stuck so we need to do this */ + WAIT_DMA(ATA_TX_DMA_NBR); + + /* set up the Etrax DMA descriptors */ + + if(e100_ide_build_dmatable (drive)) + return 1; + + if(!atapi) { + /* set the irq handler which will finish the request when DMA is done */ + + ide_set_handler(drive, &etrax_dma_intr, WAIT_CMD, NULL); + + /* issue cmd to drive */ + if ((HWGROUP(drive)->rq->cmd == IDE_DRIVE_TASKFILE) && + (drive->addressing == 1)) { + ide_task_t *args = HWGROUP(drive)->rq->special; + etrax100_ide_outb(args->tfRegister[IDE_COMMAND_OFFSET], IDE_COMMAND_REG); + } else if (drive->addressing) { + etrax100_ide_outb(WIN_WRITEDMA_EXT, IDE_COMMAND_REG); + } else { + etrax100_ide_outb(WIN_WRITEDMA, IDE_COMMAND_REG); + } + } + + /* begin DMA */ + + *R_DMA_CH2_FIRST = virt_to_phys(ata_descrs); + *R_DMA_CH2_CMD = IO_STATE(R_DMA_CH2_CMD, cmd, start); + + /* initiate a multi word dma write using DMA handshaking */ + + *R_ATA_TRANSFER_CNT = + IO_FIELD(R_ATA_TRANSFER_CNT, count, ata_tot_size >> 1); + + *R_ATA_CTRL_DATA = + IO_FIELD(R_ATA_CTRL_DATA, data, IDE_DATA_REG) | + IO_STATE(R_ATA_CTRL_DATA, rw, write) | + IO_STATE(R_ATA_CTRL_DATA, src_dst, dma) | + IO_STATE(R_ATA_CTRL_DATA, handsh, dma) | + IO_STATE(R_ATA_CTRL_DATA, multi, on) | + IO_STATE(R_ATA_CTRL_DATA, dma_size, word); + + LED_DISK_WRITE(1); + + D(printk("dma write of %d bytes.\n", ata_tot_size)); + } + return 0; +} + +static int e100_dma_write(ide_drive_t *drive) +{ + e100_read_command = 0; + /* ATAPI-devices (not disks) first call ide_dma_read/write to set the direction + * then they call ide_dma_begin after they have issued the appropriate drive command + * themselves to actually start the chipset DMA. so we just return here if we're + * not a diskdrive. + */ + if (drive->media != ide_disk) + return 0; + return e100_start_dma(drive, 0, 0); +} + +static int e100_dma_read(ide_drive_t *drive) +{ + e100_read_command = 1; + /* ATAPI-devices (not disks) first call ide_dma_read/write to set the direction + * then they call ide_dma_begin after they have issued the appropriate drive command + * themselves to actually start the chipset DMA. so we just return here if we're + * not a diskdrive. + */ + if (drive->media != ide_disk) + return 0; + return e100_start_dma(drive, 0, 1); +} + +static int e100_dma_begin(ide_drive_t *drive) +{ + /* begin DMA, used by ATAPI devices which want to issue the + * appropriate IDE command themselves. + * + * they have already called ide_dma_read/write to set the + * static reading flag, now they call ide_dma_begin to do + * the real stuff. we tell our code below not to issue + * any IDE commands itself and jump into it. + */ + return e100_start_dma(drive, 1, e100_read_command); +} diff --git a/arch/cris/kernel/ksyms.c b/arch/cris/kernel/crisksyms.c similarity index 91% rename from arch/cris/kernel/ksyms.c rename to arch/cris/kernel/crisksyms.c index 1161a2525..6ded633f8 100644 --- a/arch/cris/kernel/ksyms.c +++ b/arch/cris/kernel/crisksyms.c @@ -10,7 +10,7 @@ #include #include #include - + #include #include #include @@ -20,6 +20,7 @@ #include #include #include +#include extern void dump_thread(struct pt_regs *, struct user *); extern unsigned long get_cmos_time(void); @@ -93,4 +94,11 @@ extern void * memcpy(void *, const void *, __kernel_size_t); EXPORT_SYMBOL_NOVERS(memcpy); EXPORT_SYMBOL_NOVERS(memset); +#ifdef CONFIG_ETRAX_FAST_TIMER +/* Fast timer functions */ +EXPORT_SYMBOL(fast_timer_list); +EXPORT_SYMBOL(start_one_shot_timer); +EXPORT_SYMBOL(del_fast_timer); +EXPORT_SYMBOL(schedule_usleep); +#endif diff --git a/arch/cris/kernel/hexify.c b/arch/cris/kernel/hexify.c deleted file mode 100644 index daa331fec..000000000 --- a/arch/cris/kernel/hexify.c +++ /dev/null @@ -1,31 +0,0 @@ -#include - - -void main() -{ - int c; - int comma=0; - int count=0; - while((c=getchar())!=EOF) - { - unsigned char x=c; - if(comma) - printf(","); - else - comma=1; - if(count==8) - { - count=0; - printf("\n"); - } - if(count==0) - printf("\t"); - printf("0x%02X",c); - count++; - } - if(count) - printf("\n"); - exit(0); -} - - diff --git a/arch/i386/kernel/entry_trampoline.c b/arch/i386/kernel/entry_trampoline.c deleted file mode 100644 index 4cce52fa6..000000000 --- a/arch/i386/kernel/entry_trampoline.c +++ /dev/null @@ -1,75 +0,0 @@ -/* - * linux/arch/i386/kernel/entry_trampoline.c - * - * (C) Copyright 2003 Ingo Molnar - * - * This file contains the needed support code for 4GB userspace - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -extern char __entry_tramp_start, __entry_tramp_end, __start___entry_text; - -void __init init_entry_mappings(void) -{ -#ifdef CONFIG_X86_HIGH_ENTRY - - void *tramp; - int p; - - /* - * We need a high IDT and GDT for the 4G/4G split: - */ - trap_init_virtual_IDT(); - - __set_fixmap(FIX_ENTRY_TRAMPOLINE_0, __pa((unsigned long)&__entry_tramp_start), PAGE_KERNEL); - __set_fixmap(FIX_ENTRY_TRAMPOLINE_1, __pa((unsigned long)&__entry_tramp_start) + PAGE_SIZE, PAGE_KERNEL); - tramp = (void *)fix_to_virt(FIX_ENTRY_TRAMPOLINE_0); - - printk("mapped 4G/4G trampoline to %p.\n", tramp); - BUG_ON((void *)&__start___entry_text != tramp); - /* - * Virtual kernel stack: - */ - BUG_ON(__kmap_atomic_vaddr(KM_VSTACK_TOP) & (THREAD_SIZE-1)); - BUG_ON(sizeof(struct desc_struct)*NR_CPUS*GDT_ENTRIES > 2*PAGE_SIZE); - BUG_ON((unsigned int)&__entry_tramp_end - (unsigned int)&__entry_tramp_start > 2*PAGE_SIZE); - - /* - * set up the initial thread's virtual stack related - * fields: - */ - for (p = 0; p < ARRAY_SIZE(current->thread.stack_page); p++) - current->thread.stack_page[p] = virt_to_page((char *)current->thread_info + (p*PAGE_SIZE)); - - current->thread_info->virtual_stack = (void *)__kmap_atomic_vaddr(KM_VSTACK_TOP); - - for (p = 0; p < ARRAY_SIZE(current->thread.stack_page); p++) { - __kunmap_atomic_type(KM_VSTACK_TOP-p); - __kmap_atomic(current->thread.stack_page[p], KM_VSTACK_TOP-p); - } -#endif - current->thread_info->real_stack = (void *)current->thread_info; - current->thread_info->user_pgd = NULL; - current->thread.esp0 = (unsigned long)current->thread_info->real_stack + THREAD_SIZE; -} - - - -void __init entry_trampoline_setup(void) -{ - /* - * old IRQ entries set up by the boot code will still hang - * around - they are a sign of hw trouble anyway, now they'll - * produce a double fault message. - */ - trap_init_virtual_GDT(); -} diff --git a/arch/i386/mach-es7000/es7000.c b/arch/i386/mach-es7000/es7000.c deleted file mode 100644 index defe41e6c..000000000 --- a/arch/i386/mach-es7000/es7000.c +++ /dev/null @@ -1,279 +0,0 @@ -/* - * Written by: Garry Forsgren, Unisys Corporation - * Natalie Protasevich, Unisys Corporation - * This file contains the code to configure and interface - * with Unisys ES7000 series hardware system manager. - * - * Copyright (c) 2003 Unisys Corporation. All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it would be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write the Free Software Foundation, Inc., 59 - * Temple Place - Suite 330, Boston MA 02111-1307, USA. - * - * Contact information: Unisys Corporation, Township Line & Union Meeting - * Roads-A, Unisys Way, Blue Bell, Pennsylvania, 19424, or: - * - * http://www.unisys.com - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "es7000.h" - -/* - * ES7000 Globals - */ - -volatile unsigned long *psai = NULL; -struct mip_reg *mip_reg; -struct mip_reg *host_reg; -int mip_port; -unsigned long mip_addr, host_addr; - -/* - * Parse the OEM Table - */ - -void __init -parse_unisys_oem (char *oemptr, int oem_entries) -{ - int i; - int success = 0; - unsigned char type, size; - unsigned long val; - char *tp = NULL; - struct psai *psaip = NULL; - struct mip_reg_info *mi; - struct mip_reg *host, *mip; - - tp = oemptr; - - tp += 8; - - for (i=0; i <= oem_entries; i++) { - type = *tp++; - size = *tp++; - tp -= 2; - switch (type) { - case MIP_REG: - mi = (struct mip_reg_info *)tp; - val = MIP_RD_LO(mi->host_reg); - host_addr = val; - host = (struct mip_reg *)val; - host_reg = __va(host); - val = MIP_RD_LO(mi->mip_reg); - mip_addr = val; - mip = (struct mip_reg *)val; - mip_reg = __va(mip); - Dprintk("es7000_mipcfg: host_reg = 0x%lx \n", - (unsigned long)host_reg); - Dprintk("es7000_mipcfg: mip_reg = 0x%lx \n", - (unsigned long)mip_reg); - success++; - break; - case MIP_PSAI_REG: - psaip = (struct psai *)tp; - if (tp != NULL) { - if (psaip->addr) - psai = __va(psaip->addr); - else - psai = NULL; - success++; - } - break; - default: - break; - } - if (i == 6) break; - tp += size; - } - - if (success < 2) { - printk("\nNo ES7000 found.\n"); - es7000_plat = 0; - } else { - printk("\nEnabling ES7000 specific features...\n"); - es7000_plat = 1; - } - return; -} - -int __init -find_unisys_acpi_oem_table(unsigned long *oem_addr, int *length) -{ - struct acpi_table_rsdp *rsdp = NULL; - unsigned long rsdp_phys = 0; - struct acpi_table_header *header = NULL; - int i; - struct acpi_table_sdt sdt; - - rsdp_phys = acpi_find_rsdp(); - rsdp = __va(rsdp_phys); - if (rsdp->rsdt_address) { - struct acpi_table_rsdt *mapped_rsdt = NULL; - sdt.pa = rsdp->rsdt_address; - - header = (struct acpi_table_header *) - __acpi_map_table(sdt.pa, sizeof(struct acpi_table_header)); - if (!header) - return -ENODEV; - - sdt.count = (header->length - sizeof(struct acpi_table_header)) >> 3; - mapped_rsdt = (struct acpi_table_rsdt *) - __acpi_map_table(sdt.pa, header->length); - if (!mapped_rsdt) - return -ENODEV; - - header = &mapped_rsdt->header; - - for (i = 0; i < sdt.count; i++) - sdt.entry[i].pa = (unsigned long) mapped_rsdt->entry[i]; - }; - for (i = 0; i < sdt.count; i++) { - - header = (struct acpi_table_header *) - __acpi_map_table(sdt.entry[i].pa, - sizeof(struct acpi_table_header)); - if (!header) - continue; - if (!strncmp((char *) &header->signature, "OEM1", 4)) { - if (!strncmp((char *) &header->oem_id, "UNISYS", 6)) { - void *addr; - struct oem_table *t; - acpi_table_print(header, sdt.entry[i].pa); - t = (struct oem_table *) __acpi_map_table(sdt.entry[i].pa, header->length); - addr = (void *) __acpi_map_table(t->OEMTableAddr, t->OEMTableSize); - *length = header->length; - *oem_addr = (unsigned long) addr; - return 0; - } - } - } - printk("ES7000: did not find Unisys ACPI OEM table!\n"); - return -1; -} - -static void -es7000_spin(int n) -{ - int i = 0; - - while (i++ < n) - rep_nop(); -} - -static int __init -es7000_mip_write(struct mip_reg *mip_reg) -{ - int status = 0; - int spin; - - spin = MIP_SPIN; - while (((unsigned long long)host_reg->off_38 & - (unsigned long long)MIP_VALID) != 0) { - if (--spin <= 0) { - printk("es7000_mip_write: Timeout waiting for Host Valid Flag"); - return -1; - } - es7000_spin(MIP_SPIN); - } - - memcpy(host_reg, mip_reg, sizeof(struct mip_reg)); - outb(1, mip_port); - - spin = MIP_SPIN; - - while (((unsigned long long)mip_reg->off_38 & - (unsigned long long)MIP_VALID) == 0) { - if (--spin <= 0) { - printk("es7000_mip_write: Timeout waiting for MIP Valid Flag"); - return -1; - } - es7000_spin(MIP_SPIN); - } - - status = ((unsigned long long)mip_reg->off_0 & - (unsigned long long)0xffff0000000000) >> 48; - mip_reg->off_38 = ((unsigned long long)mip_reg->off_38 & - (unsigned long long)~MIP_VALID); - return status; -} - -int -es7000_start_cpu(int cpu, unsigned long eip) -{ - unsigned long vect = 0, psaival = 0; - - if (psai == NULL) - return -1; - - vect = ((unsigned long)__pa(eip)/0x1000) << 16; - psaival = (0x1000000 | vect | cpu); - - while (*psai & 0x1000000) - ; - - *psai = psaival; - - return 0; - -} - -int -es7000_stop_cpu(int cpu) -{ - int startup; - - if (psai == NULL) - return -1; - - startup= (0x1000000 | cpu); - - while ((*psai & 0xff00ffff) != startup) - ; - - startup = (*psai & 0xff0000) >> 16; - *psai &= 0xffffff; - - return 0; - -} - -void __init -es7000_sw_apic() -{ - if (es7000_plat) { - int mip_status; - struct mip_reg es7000_mip_reg; - - printk("ES7000: Enabling APIC mode.\n"); - memset(&es7000_mip_reg, 0, sizeof(struct mip_reg)); - es7000_mip_reg.off_0 = MIP_SW_APIC; - es7000_mip_reg.off_38 = (MIP_VALID); - while ((mip_status = es7000_mip_write(&es7000_mip_reg)) != 0) - printk("es7000_sw_apic: command failed, status = %x\n", - mip_status); - return; - } -} diff --git a/arch/i386/mach-es7000/setup.c b/arch/i386/mach-es7000/setup.c deleted file mode 100644 index 4caed0e43..000000000 --- a/arch/i386/mach-es7000/setup.c +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Machine specific setup for es7000 - */ - -#include -#include -#include -#include -#include -#include -#include - -/** - * pre_intr_init_hook - initialisation prior to setting up interrupt vectors - * - * Description: - * Perform any necessary interrupt initialisation prior to setting up - * the "ordinary" interrupt call gates. For legacy reasons, the ISA - * interrupts should be initialised here if the machine emulates a PC - * in any way. - **/void __init pre_intr_init_hook(void) -{ - init_ISA_irqs(); -} - -/* - * IRQ2 is cascade interrupt to second interrupt controller - */ -static struct irqaction irq2 = { no_action, 0, 0, "cascade", NULL, NULL}; - -/** - * intr_init_hook - post gate setup interrupt initialisation - * - * Description: - * Fill in any interrupts that may have been left out by the general - * init_IRQ() routine. interrupts having to do with the machine rather - * than the devices on the I/O bus (like APIC interrupts in intel MP - * systems) are started here. - **/ -void __init intr_init_hook(void) -{ -#ifdef CONFIG_X86_LOCAL_APIC - apic_intr_init(); -#endif - - if (!acpi_ioapic) - setup_irq(2, &irq2); -} - -/** - * pre_setup_arch_hook - hook called prior to any setup_arch() execution - * - * Description: - * generally used to activate any machine specific identification - * routines that may be needed before setup_arch() runs. On VISWS - * this is used to get the board revision and type. - **/ -void __init pre_setup_arch_hook(void) -{ -} - -/** - * trap_init_hook - initialise system specific traps - * - * Description: - * Called as the final act of trap_init(). Used in VISWS to initialise - * the various board specific APIC traps. - **/ -void __init trap_init_hook(void) -{ -} - -static struct irqaction irq0 = { timer_interrupt, SA_INTERRUPT, 0, "timer", NULL, NULL}; - -/** - * time_init_hook - do any specific initialisations for the system timer. - * - * Description: - * Must plug the system timer interrupt source at HZ into the IRQ listed - * in irq_vectors.h:TIMER_IRQ - **/ -void __init time_init_hook(void) -{ - setup_irq(0, &irq0); -} - -#ifdef CONFIG_MCA -/** - * mca_nmi_hook - hook into MCA specific NMI chain - * - * Description: - * The MCA (Microchannel Arcitecture) has an NMI chain for NMI sources - * along the MCA bus. Use this to hook into that chain if you will need - * it. - **/ -void __init mca_nmi_hook(void) -{ - /* If I recall correctly, there's a whole bunch of other things that - * we can do to check for NMI problems, but that's all I know about - * at the moment. - */ - - printk("NMI generated from unknown source!\n"); -} - -#endif diff --git a/arch/i386/mach-es7000/topology.c b/arch/i386/mach-es7000/topology.c deleted file mode 100644 index e96d8910a..000000000 --- a/arch/i386/mach-es7000/topology.c +++ /dev/null @@ -1,64 +0,0 @@ -/* - * arch/i386/mach-generic/topology.c - Populate driverfs with topology information - * - * Written by: Matthew Dobson, IBM Corporation - * Original Code: Paul Dorwin, IBM Corporation, Patrick Mochel, OSDL - * - * Copyright (C) 2002, IBM Corp. - * - * All rights reserved. - * - * 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, GOOD TITLE or - * NON INFRINGEMENT. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Send feedback to - */ -#include -#include -#include - -struct i386_cpu cpu_devices[NR_CPUS]; - -#ifdef CONFIG_NUMA -#include -#include - -struct i386_node node_devices[MAX_NUMNODES]; - -static int __init topology_init(void) -{ - int i; - - for (i = 0; i < num_online_nodes(); i++) - arch_register_node(i); - for (i = 0; i < NR_CPUS; i++) - if (cpu_possible(i)) arch_register_cpu(i); - return 0; -} - -#else /* !CONFIG_NUMA */ - -static int __init topology_init(void) -{ - int i; - - for (i = 0; i < NR_CPUS; i++) - if (cpu_possible(i)) arch_register_cpu(i); - return 0; -} - -#endif /* CONFIG_NUMA */ - -subsys_initcall(topology_init); diff --git a/arch/i386/mach-generic/es7000.c b/arch/i386/mach-generic/es7000.c new file mode 100644 index 000000000..48d3ec372 --- /dev/null +++ b/arch/i386/mach-generic/es7000.c @@ -0,0 +1,28 @@ +/* + * APIC driver for the Unisys ES7000 chipset. + */ +#define APIC_DEFINITION 1 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static __init int probe_es7000(void) +{ + /* probed later in mptable/ACPI hooks */ + return 0; +} + +struct genapic apic_es7000 = APIC_INIT("es7000", probe_es7000); diff --git a/arch/ia64/configs/sim_defconfig b/arch/ia64/configs/sim_defconfig new file mode 100644 index 000000000..5744b59f1 --- /dev/null +++ b/arch/ia64/configs/sim_defconfig @@ -0,0 +1,535 @@ +# +# Automatically generated make config: don't edit +# + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y +# CONFIG_CLEAN_COMPILE is not set +# CONFIG_STANDALONE is not set +CONFIG_BROKEN=y +CONFIG_BROKEN_ON_SMP=y + +# +# General setup +# +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +# CONFIG_POSIX_MQUEUE is not set +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_SYSCTL=y +# CONFIG_AUDIT is not set +CONFIG_LOG_BUF_SHIFT=16 +# CONFIG_HOTPLUG is not set +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +# CONFIG_EMBEDDED is not set +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_AS=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +CONFIG_OBSOLETE_MODPARM=y +CONFIG_MODVERSIONS=y +CONFIG_KMOD=y +CONFIG_STOP_MACHINE=y + +# +# Processor type and features +# +CONFIG_IA64=y +CONFIG_64BIT=y +CONFIG_MMU=y +CONFIG_RWSEM_XCHGADD_ALGORITHM=y +CONFIG_TIME_INTERPOLATION=y +CONFIG_EFI=y +# CONFIG_IA64_GENERIC is not set +# CONFIG_IA64_DIG is not set +# CONFIG_IA64_HP_ZX1 is not set +# CONFIG_IA64_SGI_SN2 is not set +CONFIG_IA64_HP_SIM=y +# CONFIG_ITANIUM is not set +CONFIG_MCKINLEY=y +# CONFIG_IA64_PAGE_SIZE_4KB is not set +# CONFIG_IA64_PAGE_SIZE_8KB is not set +# CONFIG_IA64_PAGE_SIZE_16KB is not set +CONFIG_IA64_PAGE_SIZE_64KB=y +CONFIG_IA64_L1_CACHE_SHIFT=7 +# CONFIG_MCKINLEY_ASTEP_SPECIFIC is not set +# CONFIG_VIRTUAL_MEM_MAP is not set +# CONFIG_IA64_CYCLONE is not set +CONFIG_FORCE_MAX_ZONEORDER=18 +CONFIG_SMP=y +CONFIG_NR_CPUS=64 +CONFIG_PREEMPT=y +CONFIG_HAVE_DEC_LOCK=y +CONFIG_IA32_SUPPORT=y +CONFIG_COMPAT=y +# CONFIG_PERFMON is not set +CONFIG_IA64_PALINFO=m + +# +# Firmware Drivers +# +CONFIG_EFI_VARS=y +# CONFIG_SMBIOS is not set +CONFIG_BINFMT_ELF=y +CONFIG_BINFMT_MISC=y + +# +# Power management and ACPI +# + +# +# Device Drivers +# + +# +# Generic Driver Options +# +# CONFIG_DEBUG_DRIVER is not set + +# +# Memory Technology Devices (MTD) +# +# CONFIG_MTD is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set + +# +# Plug and Play support +# + +# +# Block devices +# +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_CRYPTOLOOP is not set +# CONFIG_BLK_DEV_NBD is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=4096 +# CONFIG_BLK_DEV_INITRD is not set + +# +# ATA/ATAPI/MFM/RLL support +# +# CONFIG_IDE is not set + +# +# SCSI device support +# +CONFIG_SCSI=y +CONFIG_SCSI_PROC_FS=y + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=y +# CONFIG_CHR_DEV_ST is not set +# CONFIG_CHR_DEV_OSST is not set +# CONFIG_BLK_DEV_SR is not set +# CONFIG_CHR_DEV_SG is not set + +# +# Some SCSI devices (e.g. CD jukebox) support multiple LUNs +# +CONFIG_SCSI_MULTI_LUN=y +CONFIG_SCSI_CONSTANTS=y +CONFIG_SCSI_LOGGING=y + +# +# SCSI Transport Attributes +# +CONFIG_SCSI_SPI_ATTRS=y +# CONFIG_SCSI_FC_ATTRS is not set + +# +# SCSI low-level drivers +# +# CONFIG_SCSI_AIC7XXX_OLD is not set +# CONFIG_SCSI_SATA is not set +# CONFIG_SCSI_EATA_PIO is not set +# CONFIG_SCSI_DEBUG is not set + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set + +# +# Fusion MPT device support +# + +# +# IEEE 1394 (FireWire) support +# +# CONFIG_IEEE1394 is not set + +# +# I2O device support +# + +# +# Networking support +# +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +# CONFIG_NETLINK_DEV is not set +# CONFIG_UNIX is not set +# CONFIG_NET_KEY is not set +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +# CONFIG_IP_ADVANCED_ROUTER is not set +# CONFIG_IP_PNP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_IP_MROUTE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_IPV6 is not set +# CONFIG_NETFILTER is not set + +# +# SCTP Configuration (EXPERIMENTAL) +# +# CONFIG_IP_SCTP is not set +# CONFIG_ATM is not set +# CONFIG_BRIDGE is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_FASTROUTE is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_HAMRADIO is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +# CONFIG_NETDEVICES is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Telephony Support +# +# CONFIG_PHONE is not set + +# +# Input device support +# +CONFIG_INPUT=y + +# +# Userland interfaces +# +CONFIG_INPUT_MOUSEDEV=y +CONFIG_INPUT_MOUSEDEV_PSAUX=y +CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_TSDEV is not set +# CONFIG_INPUT_EVDEV is not set +# CONFIG_INPUT_EVBUG is not set + +# +# Input I/O drivers +# +# CONFIG_GAMEPORT is not set +CONFIG_SOUND_GAMEPORT=y +CONFIG_SERIO=y +# CONFIG_SERIO_I8042 is not set +CONFIG_SERIO_SERPORT=y +# CONFIG_SERIO_CT82C710 is not set + +# +# Input Device Drivers +# +# CONFIG_INPUT_KEYBOARD is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +CONFIG_UNIX98_PTYS=y +# CONFIG_LEGACY_PTYS is not set +# CONFIG_QIC02_TAPE is not set + +# +# IPMI +# +# CONFIG_IPMI_HANDLER is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +CONFIG_EFI_RTC=y +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set +# CONFIG_AGP is not set +# CONFIG_DRM is not set +# CONFIG_RAW_DRIVER is not set + +# +# I2C support +# +# CONFIG_I2C is not set + +# +# Misc devices +# + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# Digital Video Broadcasting Devices +# +# CONFIG_DVB is not set + +# +# Graphics support +# +# CONFIG_FB is not set + +# +# Console display driver support +# +# CONFIG_VGA_CONSOLE is not set +# CONFIG_MDA_CONSOLE is not set +CONFIG_DUMMY_CONSOLE=y + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# USB support +# + +# +# USB Gadget Support +# +# CONFIG_USB_GADGET is not set + +# +# File systems +# +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR is not set +CONFIG_EXT3_FS=y +# CONFIG_EXT3_FS_XATTR is not set +CONFIG_JBD=y +# CONFIG_JBD_DEBUG is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +# CONFIG_FAT_FS is not set +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_KCORE=y +CONFIG_SYSFS=y +# CONFIG_DEVFS_FS is not set +# CONFIG_DEVPTS_FS_XATTR is not set +# CONFIG_TMPFS is not set +CONFIG_HUGETLBFS=y +CONFIG_HUGETLB_PAGE=y +CONFIG_RAMFS=y + +# +# Miscellaneous filesystems +# +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_CRAMFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set + +# +# Network File Systems +# +CONFIG_NFS_FS=y +# CONFIG_NFS_V3 is not set +# CONFIG_NFS_V4 is not set +CONFIG_NFS_DIRECTIO=y +CONFIG_NFSD=y +CONFIG_NFSD_V3=y +# CONFIG_NFSD_V4 is not set +# CONFIG_NFSD_TCP is not set +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +CONFIG_EXPORTFS=y +CONFIG_SUNRPC=y +# CONFIG_RPCSEC_GSS_KRB5 is not set +# CONFIG_SMB_FS is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set + +# +# Partition Types +# +CONFIG_PARTITION_ADVANCED=y +# CONFIG_ACORN_PARTITION is not set +# CONFIG_OSF_PARTITION is not set +# CONFIG_AMIGA_PARTITION is not set +# CONFIG_ATARI_PARTITION is not set +# CONFIG_MAC_PARTITION is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_BSD_DISKLABEL is not set +# CONFIG_MINIX_SUBPARTITION is not set +# CONFIG_SOLARIS_X86_PARTITION is not set +# CONFIG_UNIXWARE_DISKLABEL is not set +# CONFIG_LDM_PARTITION is not set +# CONFIG_NEC98_PARTITION is not set +# CONFIG_SGI_PARTITION is not set +# CONFIG_ULTRIX_PARTITION is not set +# CONFIG_SUN_PARTITION is not set +CONFIG_EFI_PARTITION=y + +# +# Native Language Support +# +# CONFIG_NLS is not set + +# +# Library routines +# +CONFIG_CRC32=y +# CONFIG_LIBCRC32C is not set + +# +# HP Simulator drivers +# +CONFIG_HP_SIMETH=y +CONFIG_HP_SIMSERIAL=y +CONFIG_HP_SIMSERIAL_CONSOLE=y +CONFIG_HP_SIMSCSI=y + +# +# Profiling support +# +# CONFIG_PROFILING is not set + +# +# Kernel hacking +# +# CONFIG_IA64_GRANULE_16MB is not set +CONFIG_IA64_GRANULE_64MB=y +CONFIG_DEBUG_KERNEL=y +# CONFIG_IA64_PRINT_HAZARDS is not set +# CONFIG_DISABLE_VHPT is not set +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_DEBUG_SLAB is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_IA64_DEBUG_CMPXCHG is not set +# CONFIG_IA64_DEBUG_IRQ is not set +CONFIG_DEBUG_INFO=y +CONFIG_SYSVIPC_COMPAT=y + +# +# Security options +# +# CONFIG_SECURITY is not set + +# +# Cryptographic options +# +# CONFIG_CRYPTO is not set diff --git a/arch/ia64/dig/topology.c b/arch/ia64/dig/topology.c new file mode 100644 index 000000000..8dc31378b --- /dev/null +++ b/arch/ia64/dig/topology.c @@ -0,0 +1,43 @@ +/* + * arch/ia64/dig/topology.c + * Popuate driverfs with topology information. + * Derived entirely from i386/mach-default.c + * Intel Corporation - Ashok Raj + */ +#include +#include +#include +#include +#include +#include +#include + +static DEFINE_PER_CPU(struct ia64_cpu, cpu_devices); + +/* + * First Pass: simply borrowed code for now. Later should hook into + * hotplug notification for node/cpu/memory as applicable + */ + +static int arch_register_cpu(int num) +{ + struct node *parent = NULL; + +#ifdef CONFIG_NUMA + //parent = &node_devices[cpu_to_node(num)].node; +#endif + + return register_cpu(&per_cpu(cpu_devices,num).cpu, num, parent); +} + +static int __init topology_init(void) +{ + int i; + + for_each_cpu(i) { + arch_register_cpu(i); + } + return 0; +} + +subsys_initcall(topology_init); diff --git a/arch/ia64/kernel/perfmon_hpsim.h b/arch/ia64/kernel/perfmon_hpsim.h deleted file mode 100644 index 9c6fe7fc1..000000000 --- a/arch/ia64/kernel/perfmon_hpsim.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * This file contains the HP SKI Simulator PMU register description tables - * and pmc checkers used by perfmon.c. - * - * Copyright (C) 2002-2003 Hewlett Packard Co - * Stephane Eranian - * - * File mostly contributed by Ian Wienand - * - * This file is included as a dummy template so the kernel does not - * try to initalize registers the simulator can't handle. - * - * Note the simulator does not (currently) implement these registers, i.e., - * they do not count anything. But you can read/write them. - */ - -#define RDEP(x) (1UL<<(x)) - -#ifndef CONFIG_IA64_HP_SIM -#error "This file should only be included for the HP Simulator" -#endif - -static pfm_reg_desc_t pfm_hpsim_pmc_desc[PMU_MAX_PMCS]={ -/* pmc0 */ { PFM_REG_CONTROL , 0, 0x1UL, -1UL, NULL, NULL, {0UL, 0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, -/* pmc1 */ { PFM_REG_CONTROL , 0, 0x0UL, -1UL, NULL, NULL, {0UL, 0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, -/* pmc2 */ { PFM_REG_CONTROL , 0, 0x0UL, -1UL, NULL, NULL, {0UL, 0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, -/* pmc3 */ { PFM_REG_CONTROL , 0, 0x0UL, -1UL, NULL, NULL, {0UL, 0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, -/* pmc4 */ { PFM_REG_COUNTING, 0, 0x0UL, -1UL, NULL, NULL, {RDEP(4), 0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, -/* pmc5 */ { PFM_REG_COUNTING, 0, 0x0UL, -1UL, NULL, NULL, {RDEP(5), 0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, -/* pmc6 */ { PFM_REG_COUNTING, 0, 0x0UL, -1UL, NULL, NULL, {RDEP(6), 0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, -/* pmc7 */ { PFM_REG_COUNTING, 0, 0x0UL, -1UL, NULL, NULL, {RDEP(7), 0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, -/* pmc8 */ { PFM_REG_COUNTING, 0, 0x0UL, -1UL, NULL, NULL, {RDEP(8), 0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, -/* pmc9 */ { PFM_REG_COUNTING, 0, 0x0UL, -1UL, NULL, NULL, {RDEP(9), 0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, -/* pmc10 */ { PFM_REG_COUNTING, 0, 0x0UL, -1UL, NULL, NULL, {RDEP(10), 0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, -/* pmc11 */ { PFM_REG_COUNTING, 0, 0x0UL, -1UL, NULL, NULL, {RDEP(11), 0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, -/* pmc12 */ { PFM_REG_COUNTING, 0, 0x0UL, -1UL, NULL, NULL, {RDEP(12), 0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, -/* pmc13 */ { PFM_REG_COUNTING, 0, 0x0UL, -1UL, NULL, NULL, {RDEP(13), 0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, -/* pmc14 */ { PFM_REG_COUNTING, 0, 0x0UL, -1UL, NULL, NULL, {RDEP(14), 0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, -/* pmc15 */ { PFM_REG_COUNTING, 0, 0x0UL, -1UL, NULL, NULL, {RDEP(15), 0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, - { PFM_REG_END , 0, 0x0UL, -1UL, NULL, NULL, {0,}, {0,}}, /* end marker */ -}; - -static pfm_reg_desc_t pfm_hpsim_pmd_desc[PMU_MAX_PMDS]={ -/* pmd0 */ { PFM_REG_BUFFER, 0, 0x0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, -/* pmd1 */ { PFM_REG_BUFFER, 0, 0x0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, -/* pmd2 */ { PFM_REG_BUFFER, 0, 0x0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, -/* pmd3 */ { PFM_REG_BUFFER, 0, 0x0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, -/* pmd4 */ { PFM_REG_COUNTING, 0, 0x0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {RDEP(4),0UL, 0UL, 0UL}}, -/* pmd5 */ { PFM_REG_COUNTING, 0, 0x0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {RDEP(5),0UL, 0UL, 0UL}}, -/* pmd6 */ { PFM_REG_COUNTING, 0, 0x0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {RDEP(6),0UL, 0UL, 0UL}}, -/* pmd7 */ { PFM_REG_COUNTING, 0, 0x0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {RDEP(7),0UL, 0UL, 0UL}}, -/* pmd8 */ { PFM_REG_COUNTING, 0, 0x0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {RDEP(8),0UL, 0UL, 0UL}}, -/* pmd9 */ { PFM_REG_COUNTING, 0, 0x0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {RDEP(9),0UL, 0UL, 0UL}}, -/* pmd10 */ { PFM_REG_COUNTING, 0, 0x0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {RDEP(10),0UL, 0UL, 0UL}}, -/* pmd11 */ { PFM_REG_COUNTING, 0, 0x0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {RDEP(11),0UL, 0UL, 0UL}}, -/* pmd12 */ { PFM_REG_COUNTING, 0, 0x0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {RDEP(12),0UL, 0UL, 0UL}}, -/* pmd13 */ { PFM_REG_COUNTING, 0, 0x0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {RDEP(13),0UL, 0UL, 0UL}}, -/* pmd14 */ { PFM_REG_COUNTING, 0, 0x0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {RDEP(14),0UL, 0UL, 0UL}}, -/* pmd15 */ { PFM_REG_COUNTING, 0, 0x0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {RDEP(15),0UL, 0UL, 0UL}}, - { PFM_REG_END , 0, 0x0UL, -1UL, NULL, NULL, {0,}, {0,}}, /* end marker */ -}; - -/* - * impl_pmcs, impl_pmds are computed at runtime to minimize errors! - */ -static pmu_config_t pmu_conf={ - .pmu_name = "hpsim", - .pmu_family = 0x7, /* ski emulator reports as Itanium */ - .enabled = 0, - .ovfl_val = (1UL << 32) - 1, - .num_ibrs = 0, /* does not use */ - .num_dbrs = 0, /* does not use */ - .pmd_desc = pfm_hpsim_pmd_desc, - .pmc_desc = pfm_hpsim_pmc_desc -}; diff --git a/arch/ia64/lib/bitop.c b/arch/ia64/lib/bitop.c new file mode 100644 index 000000000..1c6ee49fd --- /dev/null +++ b/arch/ia64/lib/bitop.c @@ -0,0 +1,88 @@ +#include +#include +#include +#include +#include + +/* + * Find next zero bit in a bitmap reasonably efficiently.. + */ + +int __find_next_zero_bit (void *addr, unsigned long size, unsigned long offset) +{ + unsigned long *p = ((unsigned long *) addr) + (offset >> 6); + unsigned long result = offset & ~63UL; + unsigned long tmp; + + if (offset >= size) + return size; + size -= result; + offset &= 63UL; + if (offset) { + tmp = *(p++); + tmp |= ~0UL >> (64-offset); + if (size < 64) + goto found_first; + if (~tmp) + goto found_middle; + size -= 64; + result += 64; + } + while (size & ~63UL) { + if (~(tmp = *(p++))) + goto found_middle; + result += 64; + size -= 64; + } + if (!size) + return result; + tmp = *p; +found_first: + tmp |= ~0UL << size; + if (tmp == ~0UL) /* any bits zero? */ + return result + size; /* nope */ +found_middle: + return result + ffz(tmp); +} +EXPORT_SYMBOL(__find_next_zero_bit); + +/* + * Find next bit in a bitmap reasonably efficiently.. + */ +int __find_next_bit(const void *addr, unsigned long size, unsigned long offset) +{ + unsigned long *p = ((unsigned long *) addr) + (offset >> 6); + unsigned long result = offset & ~63UL; + unsigned long tmp; + + if (offset >= size) + return size; + size -= result; + offset &= 63UL; + if (offset) { + tmp = *(p++); + tmp &= ~0UL << offset; + if (size < 64) + goto found_first; + if (tmp) + goto found_middle; + size -= 64; + result += 64; + } + while (size & ~63UL) { + if ((tmp = *(p++))) + goto found_middle; + result += 64; + size -= 64; + } + if (!size) + return result; + tmp = *p; + found_first: + tmp &= ~0UL >> (64-size); + if (tmp == 0UL) /* Are any bits set? */ + return result + size; /* Nope. */ + found_middle: + return result + __ffs(tmp); +} +EXPORT_SYMBOL(__find_next_bit); diff --git a/arch/mips/au1000/common/cputable.c b/arch/mips/au1000/common/cputable.c new file mode 100644 index 000000000..26744b317 --- /dev/null +++ b/arch/mips/au1000/common/cputable.c @@ -0,0 +1,56 @@ +/* + * arch/mips/au1000/common/cputable.c + * + * Copyright (C) 2004 Dan Malek (dan@embeddededge.com) + * Copied from PowerPC and updated for Alchemy Au1xxx processors. + * + * Copyright (C) 2001 Ben. Herrenschmidt (benh@kernel.crashing.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include + +struct cpu_spec* cur_cpu_spec[NR_CPUS]; + +/* With some thought, we can probably use the mask to reduce the + * size of the table. + */ +struct cpu_spec cpu_specs[] = { + { 0xffffffff, 0x00030100, "Au1000 DA", 1, 0 }, + { 0xffffffff, 0x00030201, "Au1000 HA", 1, 0 }, + { 0xffffffff, 0x00030202, "Au1000 HB", 1, 0 }, + { 0xffffffff, 0x00030203, "Au1000 HC", 1, 1 }, + { 0xffffffff, 0x00030204, "Au1000 HD", 1, 1 }, + { 0xffffffff, 0x01030200, "Au1500 AB", 1, 1 }, + { 0xffffffff, 0x01030201, "Au1500 AC", 0, 1 }, + { 0xffffffff, 0x01030202, "Au1500 AD", 0, 1 }, + { 0xffffffff, 0x02030200, "Au1100 AB", 1, 1 }, + { 0xffffffff, 0x02030201, "Au1100 BA", 1, 1 }, + { 0xffffffff, 0x02030202, "Au1100 BC", 1, 1 }, + { 0xffffffff, 0x02030203, "Au1100 BD", 0, 1 }, + { 0xffffffff, 0x02030204, "Au1100 BE", 0, 1 }, + { 0xffffffff, 0x03030200, "Au1550 AA", 0, 1 }, + { 0x00000000, 0x00000000, "Unknown Au1xxx", 1, 0 }, +}; + +void +set_cpuspec(void) +{ + struct cpu_spec *sp; + u32 prid; + + prid = read_c0_prid(); + sp = cpu_specs; + while ((prid & sp->prid_mask) != sp->prid_value) + sp++; + cur_cpu_spec[0] = sp; +} diff --git a/arch/parisc/kernel/unwind.c b/arch/parisc/kernel/unwind.c new file mode 100644 index 000000000..ccfd5fe5a --- /dev/null +++ b/arch/parisc/kernel/unwind.c @@ -0,0 +1,295 @@ +/* + * Kernel unwinding support + * + * (c) 2002-2004 Randolph Chung + * + * Derived partially from the IA64 implementation. The PA-RISC + * Runtime Architecture Document is also a useful reference to + * understand what is happening here + */ + +/* + * J. David Anglin writes: + * + * "You have to adjust the current sp to that at the begining of the function. + * There can be up to two stack additions to allocate the frame in the + * prologue. Similar things happen in the epilogue. In the presence of + * interrupts, you have to be concerned about where you are in the function + * and what stack adjustments have taken place." + * + * For now these cases are not handled, but they should be! + */ + +#include +#include +#include +#include + +#include + +#include + +/* #define DEBUG 1 */ +#ifdef DEBUG +#define dbg(x...) printk(x) +#else +#define dbg(x...) +#endif + +extern const struct unwind_table_entry __start___unwind[]; +extern const struct unwind_table_entry __stop___unwind[]; + +static spinlock_t unwind_lock; +/* + * the kernel unwind block is not dynamically allocated so that + * we can call unwind_init as early in the bootup process as + * possible (before the slab allocator is initialized) + */ +static struct unwind_table kernel_unwind_table; +static struct unwind_table *unwind_tables, *unwind_tables_end; + + +static inline const struct unwind_table_entry * +find_unwind_entry_in_table(const struct unwind_table *table, unsigned long addr) +{ + const struct unwind_table_entry *e = 0; + unsigned long lo, hi, mid; + + addr -= table->base_addr; + + for (lo = 0, hi = table->length; lo < hi; ) + { + mid = (lo + hi) / 2; + e = &table->table[mid]; + if (addr < e->region_start) + hi = mid; + else if (addr > e->region_end) + lo = mid + 1; + else + break; + } + + return e; +} + +static inline const struct unwind_table_entry * +find_unwind_entry(unsigned long addr) +{ + struct unwind_table *table = unwind_tables; + const struct unwind_table_entry *e = NULL; + + if (addr >= kernel_unwind_table.start && + addr <= kernel_unwind_table.end) + e = find_unwind_entry_in_table(&kernel_unwind_table, addr); + else + for (; table; table = table->next) + { + if (addr >= table->start && + addr <= table->end) + e = find_unwind_entry_in_table(table, addr); + if (e) + break; + } + + return e; +} + +static void +unwind_table_init(struct unwind_table *table, const char *name, + unsigned long base_addr, unsigned long gp, + const void *table_start, const void *table_end) +{ + const struct unwind_table_entry *start = table_start; + const struct unwind_table_entry *end = table_end - 1; + + table->name = name; + table->base_addr = base_addr; + table->gp = gp; + table->start = base_addr + start->region_start; + table->end = base_addr + end->region_end; + table->table = (struct unwind_table_entry *)table_start; + table->length = end - start; + table->next = NULL; +} + +void * +unwind_table_add(const char *name, unsigned long base_addr, + unsigned long gp, + const void *start, const void *end) +{ + struct unwind_table *table; + unsigned long flags; + + table = kmalloc(sizeof(struct unwind_table), GFP_USER); + if (table == NULL) + return 0; + unwind_table_init(table, name, base_addr, gp, start, end); + spin_lock_irqsave(&unwind_lock, flags); + if (unwind_tables) + { + unwind_tables_end->next = table; + unwind_tables_end = table; + } + else + { + unwind_tables = unwind_tables_end = table; + } + spin_unlock_irqrestore(&unwind_lock, flags); + + return table; +} + +/* Called from setup_arch to import the kernel unwind info */ +static int unwind_init(void) +{ + long start, stop; + register unsigned long gp __asm__ ("r27"); + + start = (long)&__start___unwind[0]; + stop = (long)&__stop___unwind[0]; + + printk("unwind_init: start = 0x%lx, end = 0x%lx, entries = %lu\n", + start, stop, + (stop - start) / sizeof(struct unwind_table_entry)); + + unwind_table_init(&kernel_unwind_table, "kernel", KERNEL_START, + gp, + &__start___unwind[0], &__stop___unwind[0]); +#if 0 + { + int i; + for (i = 0; i < 10; i++) + { + printk("region 0x%x-0x%x\n", + __start___unwind[i].region_start, + __start___unwind[i].region_end); + } + } +#endif + return 0; +} + +static void unwind_frame_regs(struct unwind_frame_info *info) +{ + const struct unwind_table_entry *e; + unsigned long npc; + unsigned int insn; + long frame_size = 0; + int looking_for_rp, rpoffset = 0; + + e = find_unwind_entry(info->ip); + if (!e) { + unsigned long sp; + extern char _stext[], _etext[]; + + dbg("Cannot find unwind entry for 0x%lx; forced unwinding\n", info->ip); + + /* Since we are doing the unwinding blind, we don't know if + we are adjusting the stack correctly or extracting the rp + correctly. The rp is checked to see if it belongs to the + kernel text section, if not we assume we don't have a + correct stack frame and we continue to unwind the stack. + This is not quite correct, and will fail for loadable + modules. */ + sp = info->sp & ~63; + do { + info->prev_sp = sp - 64; + + /* FIXME: what happens if we unwind too far so that + sp no longer falls in a mapped kernel page? */ +#ifndef __LP64__ + info->prev_ip = *(unsigned long *)(info->prev_sp - 20); +#else + info->prev_ip = *(unsigned long *)(info->prev_sp - 16); +#endif + + sp = info->prev_sp; + } while (info->prev_ip < (unsigned long)_stext || + info->prev_ip > (unsigned long)_etext); + } else { + + dbg("e->start = 0x%x, e->end = 0x%x, Save_SP = %d, Save_RP = %d size = %u\n", + e->region_start, e->region_end, e->Save_SP, e->Save_RP, e->Total_frame_size); + + looking_for_rp = e->Save_RP; + + for (npc = e->region_start; + (frame_size < (e->Total_frame_size << 3) || looking_for_rp) && + npc < info->ip; + npc += 4) { + + insn = *(unsigned int *)npc; + + if ((insn & 0xffffc000) == 0x37de0000 || + (insn & 0xffe00000) == 0x6fc00000) { + /* ldo X(sp), sp, or stwm X,D(sp) */ + frame_size += (insn & 0x1 ? -1 << 13 : 0) | + ((insn & 0x3fff) >> 1); + } else if ((insn & 0xffe00008) == 0x7ec00008) { + /* std,ma X,D(sp) */ + frame_size += (insn & 0x1 ? -1 << 13 : 0) | + (((insn >> 4) & 0x3ff) << 3); + } else if (insn == 0x6bc23fd9) { + /* stw rp,-20(sp) */ + rpoffset = 20; + looking_for_rp = 0; + } else if (insn == 0x0fc212c1) { + /* std rp,-16(sr0,sp) */ + rpoffset = 16; + looking_for_rp = 0; + } + } + + info->prev_sp = info->sp - frame_size; + if (rpoffset) + info->prev_ip = *(unsigned long *)(info->prev_sp - rpoffset); + } +} + +void unwind_frame_init(struct unwind_frame_info *info, struct task_struct *t, + struct pt_regs *regs) +{ + memset(info, 0, sizeof(struct unwind_frame_info)); + info->t = t; + info->sp = regs->ksp; + info->ip = regs->kpc; + + dbg("(%d) Start unwind from sp=%08lx ip=%08lx\n", (int)t->pid, info->sp, info->ip); +} + +void unwind_frame_init_from_blocked_task(struct unwind_frame_info *info, struct task_struct *t) +{ + struct pt_regs *regs = &t->thread.regs; + unwind_frame_init(info, t, regs); +} + +int unwind_once(struct unwind_frame_info *next_frame) +{ + unwind_frame_regs(next_frame); + + if (next_frame->prev_sp == 0 || + next_frame->prev_ip == 0) + return -1; + + next_frame->sp = next_frame->prev_sp; + next_frame->ip = next_frame->prev_ip; + next_frame->prev_sp = 0; + next_frame->prev_ip = 0; + + dbg("(%d) Continue unwind to sp=%08lx ip=%08lx\n", (int)next_frame->t->pid, next_frame->sp, next_frame->ip); + + return 0; +} + +int unwind_to_user(struct unwind_frame_info *info) +{ + int ret; + + do { + ret = unwind_once(info); + } while (!ret && !(info->ip & 3)); + + return ret; +} + +module_init(unwind_init); diff --git a/arch/ppc/kernel/dma-mapping.c b/arch/ppc/kernel/dma-mapping.c new file mode 100644 index 000000000..c859f1127 --- /dev/null +++ b/arch/ppc/kernel/dma-mapping.c @@ -0,0 +1,439 @@ +/* + * PowerPC version derived from arch/arm/mm/consistent.c + * Copyright (C) 2001 Dan Malek (dmalek@jlc.net) + * + * Copyright (C) 2000 Russell King + * + * Consistent memory allocators. Used for DMA devices that want to + * share uncached memory with the processor core. The function return + * is the virtual address and 'dma_handle' is the physical address. + * Mostly stolen from the ARM port, with some changes for PowerPC. + * -- Dan + * + * Reorganized to get rid of the arch-specific consistent_* functions + * and provide non-coherent implementations for the DMA API. -Matt + * + * Added in_interrupt() safe dma_alloc_coherent()/dma_free_coherent() + * implementation. This is pulled straight from ARM and barely + * modified. -Matt + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#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 + +int map_page(unsigned long va, phys_addr_t pa, int flags); + +#include + +/* + * This address range defaults to a value that is safe for all + * platforms which currently set CONFIG_NOT_COHERENT_CACHE. It + * can be further configured for specific applications under + * the "Advanced Setup" menu. -Matt + */ +#define CONSISTENT_BASE (CONFIG_CONSISTENT_START) +#define CONSISTENT_END (CONFIG_CONSISTENT_START + CONFIG_CONSISTENT_SIZE) +#define CONSISTENT_OFFSET(x) (((unsigned long)(x) - CONSISTENT_BASE) >> PAGE_SHIFT) + +/* + * This is the page table (2MB) covering uncached, DMA consistent allocations + */ +static pte_t *consistent_pte; +static spinlock_t consistent_lock = SPIN_LOCK_UNLOCKED; + +/* + * VM region handling support. + * + * This should become something generic, handling VM region allocations for + * vmalloc and similar (ioremap, module space, etc). + * + * I envisage vmalloc()'s supporting vm_struct becoming: + * + * struct vm_struct { + * struct vm_region region; + * unsigned long flags; + * struct page **pages; + * unsigned int nr_pages; + * unsigned long phys_addr; + * }; + * + * get_vm_area() would then call vm_region_alloc with an appropriate + * struct vm_region head (eg): + * + * struct vm_region vmalloc_head = { + * .vm_list = LIST_HEAD_INIT(vmalloc_head.vm_list), + * .vm_start = VMALLOC_START, + * .vm_end = VMALLOC_END, + * }; + * + * However, vmalloc_head.vm_start is variable (typically, it is dependent on + * the amount of RAM found at boot time.) I would imagine that get_vm_area() + * would have to initialise this each time prior to calling vm_region_alloc(). + */ +struct vm_region { + struct list_head vm_list; + unsigned long vm_start; + unsigned long vm_end; +}; + +static struct vm_region consistent_head = { + .vm_list = LIST_HEAD_INIT(consistent_head.vm_list), + .vm_start = CONSISTENT_BASE, + .vm_end = CONSISTENT_END, +}; + +static struct vm_region * +vm_region_alloc(struct vm_region *head, size_t size, int gfp) +{ + unsigned long addr = head->vm_start, end = head->vm_end - size; + unsigned long flags; + struct vm_region *c, *new; + + new = kmalloc(sizeof(struct vm_region), gfp); + if (!new) + goto out; + + spin_lock_irqsave(&consistent_lock, flags); + + list_for_each_entry(c, &head->vm_list, vm_list) { + if ((addr + size) < addr) + goto nospc; + if ((addr + size) <= c->vm_start) + goto found; + addr = c->vm_end; + if (addr > end) + goto nospc; + } + + found: + /* + * Insert this entry _before_ the one we found. + */ + list_add_tail(&new->vm_list, &c->vm_list); + new->vm_start = addr; + new->vm_end = addr + size; + + spin_unlock_irqrestore(&consistent_lock, flags); + return new; + + nospc: + spin_unlock_irqrestore(&consistent_lock, flags); + kfree(new); + out: + return NULL; +} + +static struct vm_region *vm_region_find(struct vm_region *head, unsigned long addr) +{ + struct vm_region *c; + + list_for_each_entry(c, &head->vm_list, vm_list) { + if (c->vm_start == addr) + goto out; + } + c = NULL; + out: + return c; +} + +/* + * Allocate DMA-coherent memory space and return both the kernel remapped + * virtual and bus address for that space. + */ +void * +__dma_alloc_coherent(size_t size, dma_addr_t *handle, int gfp) +{ + struct page *page; + struct vm_region *c; + unsigned long order; + u64 mask = 0x00ffffff, limit; /* ISA default */ + + if (!consistent_pte) { + printk(KERN_ERR "%s: not initialised\n", __func__); + dump_stack(); + return NULL; + } + + size = PAGE_ALIGN(size); + limit = (mask + 1) & ~mask; + if ((limit && size >= limit) || size >= (CONSISTENT_END - CONSISTENT_BASE)) { + printk(KERN_WARNING "coherent allocation too big (requested %#x mask %#Lx)\n", + size, mask); + return NULL; + } + + order = get_order(size); + + if (mask != 0xffffffff) + gfp |= GFP_DMA; + + page = alloc_pages(gfp, order); + if (!page) + goto no_page; + + /* + * Invalidate any data that might be lurking in the + * kernel direct-mapped region for device DMA. + */ + { + unsigned long kaddr = (unsigned long)page_address(page); + memset(page_address(page), 0, size); + flush_dcache_range(kaddr, kaddr + size); + } + + /* + * Allocate a virtual address in the consistent mapping region. + */ + c = vm_region_alloc(&consistent_head, size, + gfp & ~(__GFP_DMA | __GFP_HIGHMEM)); + if (c) { + pte_t *pte = consistent_pte + CONSISTENT_OFFSET(c->vm_start); + struct page *end = page + (1 << order); + + /* + * Set the "dma handle" + */ + *handle = page_to_bus(page); + + do { + BUG_ON(!pte_none(*pte)); + + set_page_count(page, 1); + SetPageReserved(page); + set_pte(pte, mk_pte(page, pgprot_noncached(PAGE_KERNEL))); + page++; + pte++; + } while (size -= PAGE_SIZE); + + /* + * Free the otherwise unused pages. + */ + while (page < end) { + set_page_count(page, 1); + __free_page(page); + page++; + } + + return (void *)c->vm_start; + } + + if (page) + __free_pages(page, order); + no_page: + return NULL; +} + +/* + * free a page as defined by the above mapping. + */ +void __dma_free_coherent(size_t size, void *vaddr) +{ + struct vm_region *c; + unsigned long flags; + pte_t *ptep; + + size = PAGE_ALIGN(size); + + spin_lock_irqsave(&consistent_lock, flags); + + c = vm_region_find(&consistent_head, (unsigned long)vaddr); + if (!c) + goto no_area; + + if ((c->vm_end - c->vm_start) != size) { + printk(KERN_ERR "%s: freeing wrong coherent size (%ld != %d)\n", + __func__, c->vm_end - c->vm_start, size); + dump_stack(); + size = c->vm_end - c->vm_start; + } + + ptep = consistent_pte + CONSISTENT_OFFSET(c->vm_start); + do { + pte_t pte = ptep_get_and_clear(ptep); + unsigned long pfn; + + ptep++; + + if (!pte_none(pte) && pte_present(pte)) { + pfn = pte_pfn(pte); + + if (pfn_valid(pfn)) { + struct page *page = pfn_to_page(pfn); + ClearPageReserved(page); + + __free_page(page); + continue; + } + } + + printk(KERN_CRIT "%s: bad page in kernel page table\n", + __func__); + } while (size -= PAGE_SIZE); + + flush_tlb_kernel_range(c->vm_start, c->vm_end); + + list_del(&c->vm_list); + + spin_unlock_irqrestore(&consistent_lock, flags); + + kfree(c); + return; + + no_area: + spin_unlock_irqrestore(&consistent_lock, flags); + printk(KERN_ERR "%s: trying to free invalid coherent area: %p\n", + __func__, vaddr); + dump_stack(); +} +EXPORT_SYMBOL(dma_free_coherent); + +/* + * Initialise the consistent memory allocation. + */ +static int __init dma_alloc_init(void) +{ + pgd_t *pgd; + pmd_t *pmd; + pte_t *pte; + int ret = 0; + + spin_lock(&init_mm.page_table_lock); + + do { + pgd = pgd_offset(&init_mm, CONSISTENT_BASE); + pmd = pmd_alloc(&init_mm, pgd, CONSISTENT_BASE); + if (!pmd) { + printk(KERN_ERR "%s: no pmd tables\n", __func__); + ret = -ENOMEM; + break; + } + WARN_ON(!pmd_none(*pmd)); + + pte = pte_alloc_kernel(&init_mm, pmd, CONSISTENT_BASE); + if (!pte) { + printk(KERN_ERR "%s: no pte tables\n", __func__); + ret = -ENOMEM; + break; + } + + consistent_pte = pte; + } while (0); + + spin_unlock(&init_mm.page_table_lock); + + return ret; +} + +core_initcall(dma_alloc_init); + +/* + * make an area consistent. + */ +void __dma_sync(void *vaddr, size_t size, int direction) +{ + unsigned long start = (unsigned long)vaddr; + unsigned long end = start + size; + + switch (direction) { + case DMA_NONE: + BUG(); + case DMA_FROM_DEVICE: /* invalidate only */ + invalidate_dcache_range(start, end); + break; + case DMA_TO_DEVICE: /* writeback only */ + clean_dcache_range(start, end); + break; + case DMA_BIDIRECTIONAL: /* writeback and invalidate */ + flush_dcache_range(start, end); + break; + } +} + +#ifdef CONFIG_HIGHMEM +/* + * __dma_sync_page() implementation for systems using highmem. + * In this case, each page of a buffer must be kmapped/kunmapped + * in order to have a virtual address for __dma_sync(). This must + * not sleep so kmap_atmomic()/kunmap_atomic() are used. + * + * Note: yes, it is possible and correct to have a buffer extend + * beyond the first page. + */ +static inline void __dma_sync_page_highmem(struct page *page, + unsigned long offset, size_t size, int direction) +{ + size_t seg_size = min((size_t)PAGE_SIZE, size) - offset; + size_t cur_size = seg_size; + unsigned long flags, start, seg_offset = offset; + int nr_segs = PAGE_ALIGN(size + (PAGE_SIZE - offset))/PAGE_SIZE; + int seg_nr = 0; + + local_irq_save(flags); + + do { + start = (unsigned long)kmap_atomic(page + seg_nr, + KM_PPC_SYNC_PAGE) + seg_offset; + + /* Sync this buffer segment */ + __dma_sync((void *)start, seg_size, direction); + kunmap_atomic((void *)start, KM_PPC_SYNC_PAGE); + seg_nr++; + + /* Calculate next buffer segment size */ + seg_size = min((size_t)PAGE_SIZE, size - cur_size); + + /* Add the segment size to our running total */ + cur_size += seg_size; + seg_offset = 0; + } while (seg_nr < nr_segs); + + local_irq_restore(flags); +} +#endif /* CONFIG_HIGHMEM */ + +/* + * __dma_sync_page makes memory consistent. identical to __dma_sync, but + * takes a struct page instead of a virtual address + */ +void __dma_sync_page(struct page *page, unsigned long offset, + size_t size, int direction) +{ +#ifdef CONFIG_HIGHMEM + __dma_sync_page_highmem(page, offset, size, direction); +#else + unsigned long start = (unsigned long)page_address(page) + offset; + __dma_sync((void *)start, size, direction); +#endif +} diff --git a/arch/ppc/kernel/vecemu.c b/arch/ppc/kernel/vecemu.c new file mode 100644 index 000000000..1430ef592 --- /dev/null +++ b/arch/ppc/kernel/vecemu.c @@ -0,0 +1,346 @@ +/* + * Routines to emulate some Altivec/VMX instructions, specifically + * those that can trap when given denormalized operands in Java mode. + */ +#include +#include +#include +#include +#include +#include + +/* Functions in vector.S */ +extern void vaddfp(vector128 *dst, vector128 *a, vector128 *b); +extern void vsubfp(vector128 *dst, vector128 *a, vector128 *b); +extern void vmaddfp(vector128 *dst, vector128 *a, vector128 *b, vector128 *c); +extern void vnmsubfp(vector128 *dst, vector128 *a, vector128 *b, vector128 *c); +extern void vrefp(vector128 *dst, vector128 *src); +extern void vrsqrtefp(vector128 *dst, vector128 *src); +extern void vexptep(vector128 *dst, vector128 *src); + +static unsigned int exp2s[8] = { + 0x800000, + 0x8b95c2, + 0x9837f0, + 0xa5fed7, + 0xb504f3, + 0xc5672a, + 0xd744fd, + 0xeac0c7 +}; + +/* + * Computes an estimate of 2^x. The `s' argument is the 32-bit + * single-precision floating-point representation of x. + */ +static unsigned int eexp2(unsigned int s) +{ + int exp, pwr; + unsigned int mant, frac; + + /* extract exponent field from input */ + exp = ((s >> 23) & 0xff) - 127; + if (exp > 7) { + /* check for NaN input */ + if (exp == 128 && (s & 0x7fffff) != 0) + return s | 0x400000; /* return QNaN */ + /* 2^-big = 0, 2^+big = +Inf */ + return (s & 0x80000000)? 0: 0x7f800000; /* 0 or +Inf */ + } + if (exp < -23) + return 0x3f800000; /* 1.0 */ + + /* convert to fixed point integer in 9.23 representation */ + pwr = (s & 0x7fffff) | 0x800000; + if (exp > 0) + pwr <<= exp; + else + pwr >>= -exp; + if (s & 0x80000000) + pwr = -pwr; + + /* extract integer part, which becomes exponent part of result */ + exp = (pwr >> 23) + 126; + if (exp >= 254) + return 0x7f800000; + if (exp < -23) + return 0; + + /* table lookup on top 3 bits of fraction to get mantissa */ + mant = exp2s[(pwr >> 20) & 7]; + + /* linear interpolation using remaining 20 bits of fraction */ + asm("mulhwu %0,%1,%2" : "=r" (frac) + : "r" (pwr << 12), "r" (0x172b83ff)); + asm("mulhwu %0,%1,%2" : "=r" (frac) : "r" (frac), "r" (mant)); + mant += frac; + + if (exp >= 0) + return mant + (exp << 23); + + /* denormalized result */ + exp = -exp; + mant += 1 << (exp - 1); + return mant >> exp; +} + +/* + * Computes an estimate of log_2(x). The `s' argument is the 32-bit + * single-precision floating-point representation of x. + */ +static unsigned int elog2(unsigned int s) +{ + int exp, mant, lz, frac; + + exp = s & 0x7f800000; + mant = s & 0x7fffff; + if (exp == 0x7f800000) { /* Inf or NaN */ + if (mant != 0) + s |= 0x400000; /* turn NaN into QNaN */ + return s; + } + if ((exp | mant) == 0) /* +0 or -0 */ + return 0xff800000; /* return -Inf */ + + if (exp == 0) { + /* denormalized */ + asm("cntlzw %0,%1" : "=r" (lz) : "r" (mant)); + mant <<= lz - 8; + exp = (-118 - lz) << 23; + } else { + mant |= 0x800000; + exp -= 127 << 23; + } + + if (mant >= 0xb504f3) { /* 2^0.5 * 2^23 */ + exp |= 0x400000; /* 0.5 * 2^23 */ + asm("mulhwu %0,%1,%2" : "=r" (mant) + : "r" (mant), "r" (0xb504f334)); /* 2^-0.5 * 2^32 */ + } + if (mant >= 0x9837f0) { /* 2^0.25 * 2^23 */ + exp |= 0x200000; /* 0.25 * 2^23 */ + asm("mulhwu %0,%1,%2" : "=r" (mant) + : "r" (mant), "r" (0xd744fccb)); /* 2^-0.25 * 2^32 */ + } + if (mant >= 0x8b95c2) { /* 2^0.125 * 2^23 */ + exp |= 0x100000; /* 0.125 * 2^23 */ + asm("mulhwu %0,%1,%2" : "=r" (mant) + : "r" (mant), "r" (0xeac0c6e8)); /* 2^-0.125 * 2^32 */ + } + if (mant > 0x800000) { /* 1.0 * 2^23 */ + /* calculate (mant - 1) * 1.381097463 */ + /* 1.381097463 == 0.125 / (2^0.125 - 1) */ + asm("mulhwu %0,%1,%2" : "=r" (frac) + : "r" ((mant - 0x800000) << 1), "r" (0xb0c7cd3a)); + exp += frac; + } + s = exp & 0x80000000; + if (exp != 0) { + if (s) + exp = -exp; + asm("cntlzw %0,%1" : "=r" (lz) : "r" (exp)); + lz = 8 - lz; + if (lz > 0) + exp >>= lz; + else if (lz < 0) + exp <<= -lz; + s += ((lz + 126) << 23) + exp; + } + return s; +} + +#define VSCR_SAT 1 + +static int ctsxs(unsigned int x, int scale, unsigned int *vscrp) +{ + int exp, mant; + + exp = (x >> 23) & 0xff; + mant = x & 0x7fffff; + if (exp == 255 && mant != 0) + return 0; /* NaN -> 0 */ + exp = exp - 127 + scale; + if (exp < 0) + return 0; /* round towards zero */ + if (exp >= 31) { + /* saturate, unless the result would be -2^31 */ + if (x + (scale << 23) != 0xcf000000) + *vscrp |= VSCR_SAT; + return (x & 0x80000000)? 0x80000000: 0x7fffffff; + } + mant |= 0x800000; + mant = (mant << 7) >> (30 - exp); + return (x & 0x80000000)? -mant: mant; +} + +static unsigned int ctuxs(unsigned int x, int scale, unsigned int *vscrp) +{ + int exp; + unsigned int mant; + + exp = (x >> 23) & 0xff; + mant = x & 0x7fffff; + if (exp == 255 && mant != 0) + return 0; /* NaN -> 0 */ + exp = exp - 127 + scale; + if (exp < 0) + return 0; /* round towards zero */ + if (x & 0x80000000) { + /* negative => saturate to 0 */ + *vscrp |= VSCR_SAT; + return 0; + } + if (exp >= 32) { + /* saturate */ + *vscrp |= VSCR_SAT; + return 0xffffffff; + } + mant |= 0x800000; + mant = (mant << 8) >> (31 - exp); + return mant; +} + +/* Round to floating integer, towards 0 */ +static unsigned int rfiz(unsigned int x) +{ + int exp; + + exp = ((x >> 23) & 0xff) - 127; + if (exp == 128 && (x & 0x7fffff) != 0) + return x | 0x400000; /* NaN -> make it a QNaN */ + if (exp >= 23) + return x; /* it's an integer already (or Inf) */ + if (exp < 0) + return x & 0x80000000; /* |x| < 1.0 rounds to 0 */ + return x & ~(0x7fffff >> exp); +} + +/* Round to floating integer, towards +/- Inf */ +static unsigned int rfii(unsigned int x) +{ + int exp, mask; + + exp = ((x >> 23) & 0xff) - 127; + if (exp == 128 && (x & 0x7fffff) != 0) + return x | 0x400000; /* NaN -> make it a QNaN */ + if (exp >= 23) + return x; /* it's an integer already (or Inf) */ + if ((x & 0x7fffffff) == 0) + return x; /* +/-0 -> +/-0 */ + if (exp < 0) + /* 0 < |x| < 1.0 rounds to +/- 1.0 */ + return (x & 0x80000000) | 0x3f800000; + mask = 0x7fffff >> exp; + /* mantissa overflows into exponent - that's OK, + it can't overflow into the sign bit */ + return (x + mask) & ~mask; +} + +/* Round to floating integer, to nearest */ +static unsigned int rfin(unsigned int x) +{ + int exp, half; + + exp = ((x >> 23) & 0xff) - 127; + if (exp == 128 && (x & 0x7fffff) != 0) + return x | 0x400000; /* NaN -> make it a QNaN */ + if (exp >= 23) + return x; /* it's an integer already (or Inf) */ + if (exp < -1) + return x & 0x80000000; /* |x| < 0.5 -> +/-0 */ + if (exp == -1) + /* 0.5 <= |x| < 1.0 rounds to +/- 1.0 */ + return (x & 0x80000000) | 0x3f800000; + half = 0x400000 >> exp; + /* add 0.5 to the magnitude and chop off the fraction bits */ + return (x + half) & ~(0x7fffff >> exp); +} + +int +emulate_altivec(struct pt_regs *regs) +{ + unsigned int instr, i; + unsigned int va, vb, vc, vd; + vector128 *vrs; + + if (get_user(instr, (unsigned int *) regs->nip)) + return -EFAULT; + if ((instr >> 26) != 4) + return -EINVAL; /* not an altivec instruction */ + vd = (instr >> 21) & 0x1f; + va = (instr >> 16) & 0x1f; + vb = (instr >> 11) & 0x1f; + vc = (instr >> 6) & 0x1f; + + vrs = current->thread.vr; + switch (instr & 0x3f) { + case 10: + switch (vc) { + case 0: /* vaddfp */ + vaddfp(&vrs[vd], &vrs[va], &vrs[vb]); + break; + case 1: /* vsubfp */ + vsubfp(&vrs[vd], &vrs[va], &vrs[vb]); + break; + case 4: /* vrefp */ + vrefp(&vrs[vd], &vrs[vb]); + break; + case 5: /* vrsqrtefp */ + vrsqrtefp(&vrs[vd], &vrs[vb]); + break; + case 6: /* vexptefp */ + for (i = 0; i < 4; ++i) + vrs[vd].u[i] = eexp2(vrs[vb].u[i]); + break; + case 7: /* vlogefp */ + for (i = 0; i < 4; ++i) + vrs[vd].u[i] = elog2(vrs[vb].u[i]); + break; + case 8: /* vrfin */ + for (i = 0; i < 4; ++i) + vrs[vd].u[i] = rfin(vrs[vb].u[i]); + break; + case 9: /* vrfiz */ + for (i = 0; i < 4; ++i) + vrs[vd].u[i] = rfiz(vrs[vb].u[i]); + break; + case 10: /* vrfip */ + for (i = 0; i < 4; ++i) { + u32 x = vrs[vb].u[i]; + x = (x & 0x80000000)? rfiz(x): rfii(x); + vrs[vd].u[i] = x; + } + break; + case 11: /* vrfim */ + for (i = 0; i < 4; ++i) { + u32 x = vrs[vb].u[i]; + x = (x & 0x80000000)? rfii(x): rfiz(x); + vrs[vd].u[i] = x; + } + break; + case 14: /* vctuxs */ + for (i = 0; i < 4; ++i) + vrs[vd].u[i] = ctuxs(vrs[vb].u[i], va, + ¤t->thread.vscr.u[3]); + break; + case 15: /* vctsxs */ + for (i = 0; i < 4; ++i) + vrs[vd].u[i] = ctsxs(vrs[vb].u[i], va, + ¤t->thread.vscr.u[3]); + break; + default: + return -EINVAL; + } + break; + case 46: /* vmaddfp */ + vmaddfp(&vrs[vd], &vrs[va], &vrs[vb], &vrs[vc]); + break; + case 47: /* vnmsubfp */ + vnmsubfp(&vrs[vd], &vrs[va], &vrs[vb], &vrs[vc]); + break; + default: + return -EINVAL; + } + + return 0; +} diff --git a/arch/ppc/kernel/vector.S b/arch/ppc/kernel/vector.S new file mode 100644 index 000000000..d8fe6b5fb --- /dev/null +++ b/arch/ppc/kernel/vector.S @@ -0,0 +1,217 @@ +#include +#include + +/* + * The routines below are in assembler so we can closely control the + * usage of floating-point registers. These routines must be called + * with preempt disabled. + */ + .data +fpzero: + .long 0 +fpone: + .long 0x3f800000 /* 1.0 in single-precision FP */ +fphalf: + .long 0x3f000000 /* 0.5 in single-precision FP */ + + .text +/* + * Internal routine to enable floating point and set FPSCR to 0. + * Don't call it from C; it doesn't use the normal calling convention. + */ +fpenable: + mfmsr r10 + ori r11,r10,MSR_FP + mtmsr r11 + isync + stfd fr0,24(r1) + stfd fr1,16(r1) + stfd fr31,8(r1) + lis r11,fpzero@ha + mffs fr31 + lfs fr1,fpzero@l(r11) + mtfsf 0xff,fr1 + blr + +fpdisable: + mtfsf 0xff,fr31 + lfd fr31,8(r1) + lfd fr1,16(r1) + lfd fr0,24(r1) + mtmsr r10 + isync + blr + +/* + * Vector add, floating point. + */ + .globl vaddfp +vaddfp: + stwu r1,-32(r1) + mflr r0 + stw r0,36(r1) + bl fpenable + li r0,4 + mtctr r0 + li r6,0 +1: lfsx fr0,r4,r6 + lfsx fr1,r5,r6 + fadds fr0,fr0,fr1 + stfsx fr0,r3,r6 + addi r6,r6,4 + bdnz 1b + bl fpdisable + lwz r0,36(r1) + mtlr r0 + addi r1,r1,32 + blr + +/* + * Vector subtract, floating point. + */ + .globl vsubfp +vsubfp: + stwu r1,-32(r1) + mflr r0 + stw r0,36(r1) + bl fpenable + li r0,4 + mtctr r0 + li r6,0 +1: lfsx fr0,r4,r6 + lfsx fr1,r5,r6 + fsubs fr0,fr0,fr1 + stfsx fr0,r3,r6 + addi r6,r6,4 + bdnz 1b + bl fpdisable + lwz r0,36(r1) + mtlr r0 + addi r1,r1,32 + blr + +/* + * Vector multiply and add, floating point. + */ + .globl vmaddfp +vmaddfp: + stwu r1,-48(r1) + mflr r0 + stw r0,52(r1) + bl fpenable + stfd fr2,32(r1) + li r0,4 + mtctr r0 + li r7,0 +1: lfsx fr0,r4,r7 + lfsx fr1,r5,r7 + lfsx fr2,r6,r7 + fmadds fr0,fr0,fr1,fr2 + stfsx fr0,r3,r7 + addi r7,r7,4 + bdnz 1b + lfd fr2,32(r1) + bl fpdisable + lwz r0,52(r1) + mtlr r0 + addi r1,r1,48 + blr + +/* + * Vector negative multiply and subtract, floating point. + */ + .globl vnmsubfp +vnmsubfp: + stwu r1,-48(r1) + mflr r0 + stw r0,52(r1) + bl fpenable + stfd fr2,32(r1) + li r0,4 + mtctr r0 + li r7,0 +1: lfsx fr0,r4,r7 + lfsx fr1,r5,r7 + lfsx fr2,r6,r7 + fnmsubs fr0,fr0,fr1,fr2 + stfsx fr0,r3,r7 + addi r7,r7,4 + bdnz 1b + lfd fr2,32(r1) + bl fpdisable + lwz r0,52(r1) + mtlr r0 + addi r1,r1,48 + blr + +/* + * Vector reciprocal estimate. We just compute 1.0/x. + * r3 -> destination, r4 -> source. + */ + .globl vrefp +vrefp: + stwu r1,-32(r1) + mflr r0 + stw r0,36(r1) + bl fpenable + lis r9,fpone@ha + li r0,4 + lfs fr1,fpone@l(r9) + mtctr r0 + li r6,0 +1: lfsx fr0,r4,r6 + fdivs fr0,fr1,fr0 + stfsx fr0,r3,r6 + addi r6,r6,4 + bdnz 1b + bl fpdisable + lwz r0,36(r1) + mtlr r0 + addi r1,r1,32 + blr + +/* + * Vector reciprocal square-root estimate, floating point. + * We use the frsqrte instruction for the initial estimate followed + * by 2 iterations of Newton-Raphson to get sufficient accuracy. + * r3 -> destination, r4 -> source. + */ + .globl vrsqrtefp +vrsqrtefp: + stwu r1,-48(r1) + mflr r0 + stw r0,52(r1) + bl fpenable + stfd fr2,32(r1) + stfd fr3,40(r1) + stfd fr4,48(r1) + stfd fr5,56(r1) + lis r9,fpone@ha + lis r8,fphalf@ha + li r0,4 + lfs fr4,fpone@l(r9) + lfs fr5,fphalf@l(r8) + mtctr r0 + li r6,0 +1: lfsx fr0,r4,r6 + frsqrte fr1,fr0 /* r = frsqrte(s) */ + fmuls fr3,fr1,fr0 /* r * s */ + fmuls fr2,fr1,fr5 /* r * 0.5 */ + fnmsubs fr3,fr1,fr3,fr4 /* 1 - s * r * r */ + fmadds fr1,fr2,fr3,fr1 /* r = r + 0.5 * r * (1 - s * r * r) */ + fmuls fr3,fr1,fr0 /* r * s */ + fmuls fr2,fr1,fr5 /* r * 0.5 */ + fnmsubs fr3,fr1,fr3,fr4 /* 1 - s * r * r */ + fmadds fr1,fr2,fr3,fr1 /* r = r + 0.5 * r * (1 - s * r * r) */ + stfsx fr1,r3,r6 + addi r6,r6,4 + bdnz 1b + lfd fr5,56(r1) + lfd fr4,48(r1) + lfd fr3,40(r1) + lfd fr2,32(r1) + bl fpdisable + lwz r0,36(r1) + mtlr r0 + addi r1,r1,32 + blr diff --git a/arch/ppc/mm/cachemap.c b/arch/ppc/mm/cachemap.c deleted file mode 100644 index 2033eec9b..000000000 --- a/arch/ppc/mm/cachemap.c +++ /dev/null @@ -1,174 +0,0 @@ -/* - * PowerPC version derived from arch/arm/mm/consistent.c - * Copyright (C) 2001 Dan Malek (dmalek@jlc.net) - * - * arch/ppc/mm/cachemap.c - * - * Copyright (C) 2000 Russell King - * - * Consistent memory allocators. Used for DMA devices that want to - * share uncached memory with the processor core. The function return - * is the virtual address and 'dma_handle' is the physical address. - * Mostly stolen from the ARM port, with some changes for PowerPC. - * -- Dan - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#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 - -int map_page(unsigned long va, phys_addr_t pa, int flags); - -/* This function will allocate the requested contiguous pages and - * map them into the kernel's vmalloc() space. This is done so we - * get unique mapping for these pages, outside of the kernel's 1:1 - * virtual:physical mapping. This is necessary so we can cover large - * portions of the kernel with single large page TLB entries, and - * still get unique uncached pages for consistent DMA. - */ -void *consistent_alloc(int gfp, size_t size, dma_addr_t *dma_handle) -{ - int order, err; - struct page *page, *free, *end; - phys_addr_t pa; - unsigned long flags, offset; - struct vm_struct *area = NULL; - unsigned long va = 0; - - BUG_ON(in_interrupt()); - - /* Only allocate page size areas */ - size = PAGE_ALIGN(size); - order = get_order(size); - - free = page = alloc_pages(gfp, order); - if (! page) - return NULL; - - pa = page_to_phys(page); - *dma_handle = page_to_bus(page); - end = page + (1 << order); - - /* - * we need to ensure that there are no cachelines in use, - * or worse dirty in this area. - */ - invalidate_dcache_range((unsigned long)page_address(page), - (unsigned long)page_address(page) + size); - - /* - * alloc_pages() expects the block to be handled as a unit, so - * it only sets the page count on the first page. We set the - * counts on each page so they can be freed individually - */ - for (; page < end; page++) - set_page_count(page, 1); - - - /* Allocate some common virtual space to map the new pages*/ - area = get_vm_area(size, VM_ALLOC); - if (! area) - goto out; - - va = (unsigned long) area->addr; - - flags = _PAGE_KERNEL | _PAGE_NO_CACHE; - - for (offset = 0; offset < size; offset += PAGE_SIZE) { - err = map_page(va+offset, pa+offset, flags); - if (err) { - vfree((void *)va); - va = 0; - goto out; - } - - free++; - } - - out: - /* Free pages which weren't mapped */ - for (; free < end; free++) { - __free_page(free); - } - - return (void *)va; -} - -/* - * free page(s) as defined by the above mapping. - */ -void consistent_free(void *vaddr) -{ - BUG_ON(in_interrupt()); - vfree(vaddr); -} - -/* - * make an area consistent. - */ -void consistent_sync(void *vaddr, size_t size, int direction) -{ - unsigned long start = (unsigned long)vaddr; - unsigned long end = start + size; - - switch (direction) { - case DMA_NONE: - BUG(); - case DMA_FROM_DEVICE: /* invalidate only */ - invalidate_dcache_range(start, end); - break; - case DMA_TO_DEVICE: /* writeback only */ - clean_dcache_range(start, end); - break; - case DMA_BIDIRECTIONAL: /* writeback and invalidate */ - flush_dcache_range(start, end); - break; - } -} - -/* - * consistent_sync_page make a page are consistent. identical - * to consistent_sync, but takes a struct page instead of a virtual address - */ - -void consistent_sync_page(struct page *page, unsigned long offset, - size_t size, int direction) -{ - unsigned long start; - - start = (unsigned long)page_address(page) + offset; - consistent_sync((void *)start, size, direction); -} - -EXPORT_SYMBOL(consistent_sync_page); diff --git a/arch/ppc/ocp/Makefile b/arch/ppc/ocp/Makefile deleted file mode 100644 index f669ee042..000000000 --- a/arch/ppc/ocp/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -# -# Makefile for the linux kernel. -# - -obj-y := ocp.o ocp-driver.o ocp-probe.o - diff --git a/arch/ppc/ocp/ocp-driver.c b/arch/ppc/ocp/ocp-driver.c deleted file mode 100644 index 9f6bb3f42..000000000 --- a/arch/ppc/ocp/ocp-driver.c +++ /dev/null @@ -1,195 +0,0 @@ -/* - * FILE NAME: ocp-driver.c - * - * BRIEF MODULE DESCRIPTION: - * driver callback, id matching and registration - * Based on drivers/pci/pci-driver, Copyright (c) 1997--1999 Martin Mares - * - * Maintained by: Armin - * - * - * 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 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. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include -#include -#include - -/* - * Registration of OCP drivers and handling of hot-pluggable devices. - */ - -static int -ocp_device_probe(struct device *dev) -{ - int error = 0; - struct ocp_driver *drv; - struct ocp_device *ocp_dev; - - drv = to_ocp_driver(dev->driver); - ocp_dev = to_ocp_dev(dev); - - if (drv->probe) { - error = drv->probe(ocp_dev); - DBG("probe return code %d\n", error); - if (error >= 0) { - ocp_dev->driver = drv; - error = 0; - } - } - return error; -} - -static int -ocp_device_remove(struct device *dev) -{ - struct ocp_device *ocp_dev = to_ocp_dev(dev); - - if (ocp_dev->driver) { - if (ocp_dev->driver->remove) - ocp_dev->driver->remove(ocp_dev); - ocp_dev->driver = NULL; - } - return 0; -} - -static int -ocp_device_suspend(struct device *dev, u32 state, u32 level) -{ - struct ocp_device *ocp_dev = to_ocp_dev(dev); - - int error = 0; - - if (ocp_dev->driver) { - if (level == SUSPEND_SAVE_STATE && ocp_dev->driver->save_state) - error = ocp_dev->driver->save_state(ocp_dev, state); - else if (level == SUSPEND_POWER_DOWN - && ocp_dev->driver->suspend) - error = ocp_dev->driver->suspend(ocp_dev, state); - } - return error; -} - -static int -ocp_device_resume(struct device *dev, u32 level) -{ - struct ocp_device *ocp_dev = to_ocp_dev(dev); - - if (ocp_dev->driver) { - if (level == RESUME_POWER_ON && ocp_dev->driver->resume) - ocp_dev->driver->resume(ocp_dev); - } - return 0; -} - -/** - * ocp_bus_match - Works out whether an OCP device matches any - * of the IDs listed for a given OCP driver. - * @dev: the generic device struct for the OCP device - * @drv: the generic driver struct for the OCP driver - * - * Used by a driver to check whether a OCP device present in the - * system is in its list of supported devices. Returns 1 for a - * match, or 0 if there is no match. - */ -static int -ocp_bus_match(struct device *dev, struct device_driver *drv) -{ - struct ocp_device *ocp_dev = to_ocp_dev(dev); - struct ocp_driver *ocp_drv = to_ocp_driver(drv); - const struct ocp_device_id *ids = ocp_drv->id_table; - - if (!ids) - return 0; - - while (ids->vendor || ids->device) { - if ((ids->vendor == OCP_ANY_ID - || ids->vendor == ocp_dev->vendor) - && (ids->device == OCP_ANY_ID - || ids->device == ocp_dev->device)) { - DBG("Bus match -vendor:%x device:%x\n", ids->vendor, - ids->device); - return 1; - } - ids++; - } - return 0; -} - -struct bus_type ocp_bus_type = { - .name = "ocp", - .match = ocp_bus_match, -}; - -static int __init -ocp_driver_init(void) -{ - return bus_register(&ocp_bus_type); -} - -postcore_initcall(ocp_driver_init); - -/** - * ocp_register_driver - register a new ocp driver - * @drv: the driver structure to register - * - * Adds the driver structure to the list of registered drivers - * Returns the number of ocp devices which were claimed by the driver - * during registration. The driver remains registered even if the - * return value is zero. - */ -int -ocp_register_driver(struct ocp_driver *drv) -{ - int count = 0; - - /* initialize common driver fields */ - drv->driver.name = drv->name; - drv->driver.bus = &ocp_bus_type; - drv->driver.probe = ocp_device_probe; - drv->driver.resume = ocp_device_resume; - drv->driver.suspend = ocp_device_suspend; - drv->driver.remove = ocp_device_remove; - - /* register with core */ - count = driver_register(&drv->driver); - return count ? count : 1; -} - -/** - * ocp_unregister_driver - unregister a ocp driver - * @drv: the driver structure to unregister - * - * Deletes the driver structure from the list of registered OCP drivers, - * gives it a chance to clean up by calling its remove() function for - * each device it was responsible for, and marks those devices as - * driverless. - */ - -void -ocp_unregister_driver(struct ocp_driver *drv) -{ - driver_unregister(&drv->driver); -} - -EXPORT_SYMBOL(ocp_register_driver); -EXPORT_SYMBOL(ocp_unregister_driver); -EXPORT_SYMBOL(ocp_bus_type); diff --git a/arch/ppc/ocp/ocp-probe.c b/arch/ppc/ocp/ocp-probe.c deleted file mode 100644 index bb4aff7a6..000000000 --- a/arch/ppc/ocp/ocp-probe.c +++ /dev/null @@ -1,113 +0,0 @@ -/* - * FILE NAME: ocp-probe.c - * - * BRIEF MODULE DESCRIPTION: - * Device scanning & bus set routines - * Based on drivers/pci/probe, Copyright (c) 1997--1999 Martin Mares - * - * Maintained by: Armin - * - * - * 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 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. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include -#include -#include -#include -#include - -LIST_HEAD(ocp_devices); -struct device *ocp_bus; - -static struct ocp_device * __devinit -ocp_setup_dev(struct ocp_def *odef, unsigned int index) -{ - struct ocp_device *dev; - - dev = kmalloc(sizeof(*dev), GFP_KERNEL); - if (!dev) - return NULL; - memset(dev, 0, sizeof(*dev)); - - dev->vendor = odef->vendor; - dev->device = odef->device; - dev->num = ocp_get_num(dev->device); - dev->paddr = odef->paddr; - dev->irq = odef->irq; - dev->pm = odef->pm; - dev->current_state = 4; - - sprintf(dev->name, "OCP device %04x:%04x", dev->vendor, dev->device); - - DBG("%s %s 0x%lx irq:%d pm:0x%lx \n", dev->slot_name, dev->name, - (unsigned long) dev->paddr, dev->irq, dev->pm); - - /* now put in global tree */ - sprintf(dev->dev.bus_id, "%d", index); - dev->dev.parent = ocp_bus; - dev->dev.bus = &ocp_bus_type; - device_register(&dev->dev); - - return dev; -} - -static struct device * __devinit ocp_alloc_primary_bus(void) -{ - struct device *b; - - b = kmalloc(sizeof(struct device), GFP_KERNEL); - if (b == NULL) - return NULL; - memset(b, 0, sizeof(struct device)); - strcpy(b->bus_id, "ocp"); - - device_register(b); - - return b; -} - -void __devinit ocp_setup_devices(struct ocp_def *odef) -{ - int index; - struct ocp_device *dev; - - if (ocp_bus == NULL) - ocp_bus = ocp_alloc_primary_bus(); - for (index = 0; odef->vendor != OCP_VENDOR_INVALID; ++index, ++odef) { - dev = ocp_setup_dev(odef, index); - if (dev != NULL) - list_add_tail(&dev->global_list, &ocp_devices); - } -} - -extern struct ocp_def core_ocp[]; - -static int __init -ocparch_init(void) -{ - ocp_setup_devices(core_ocp); - return 0; -} - -subsys_initcall(ocparch_init); - -EXPORT_SYMBOL(ocp_devices); diff --git a/arch/ppc/ocp/ocp.c b/arch/ppc/ocp/ocp.c deleted file mode 100644 index 8df60d79f..000000000 --- a/arch/ppc/ocp/ocp.c +++ /dev/null @@ -1,109 +0,0 @@ -/* - * ocp.c - * - * The is drived from pci.c - * - * Current Maintainer - * Armin Kuster akuster@dslextreme.com - * Jan, 2002 - * - * - * - * 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 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. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/** - * ocp_get_num - This determines how many OCP devices of a given - * device are registered - * @device: OCP device such as HOST, PCI, GPT, UART, OPB, IIC, GPIO, EMAC, ZMII, - * - * The routine returns the number that devices which is registered - */ -unsigned int ocp_get_num(unsigned int device) -{ - unsigned int count = 0; - struct ocp_device *ocp; - struct list_head *ocp_l; - - list_for_each(ocp_l, &ocp_devices) { - ocp = list_entry(ocp_l, struct ocp_device, global_list); - if (device == ocp->device) - count++; - } - return count; -} - -/** - * ocp_get_dev - get ocp driver pointer for ocp device and instance of it - * @device: OCP device such as PCI, GPT, UART, OPB, IIC, GPIO, EMAC, ZMII - * @dev_num: ocp device number whos paddr you want - * - * The routine returns ocp device pointer - * in list based on device and instance of that device - * - */ -struct ocp_device * -ocp_get_dev(unsigned int device, int dev_num) -{ - struct ocp_device *ocp; - struct list_head *ocp_l; - int count = 0; - - list_for_each(ocp_l, &ocp_devices) { - ocp = list_entry(ocp_l, struct ocp_device, global_list); - if (device == ocp->device) { - if (dev_num == count) - return ocp; - count++; - } - } - return NULL; -} - -EXPORT_SYMBOL(ocp_get_dev); -EXPORT_SYMBOL(ocp_get_num); - -#ifdef CONFIG_PM -int ocp_generic_suspend(struct ocp_device *pdev, u32 state) -{ - ocp_force_power_off(pdev); - return 0; -} - -int ocp_generic_resume(struct ocp_device *pdev) -{ - ocp_force_power_on(pdev); -} - -EXPORT_SYMBOL(ocp_generic_suspend); -EXPORT_SYMBOL(ocp_generic_resume); -#endif /* CONFIG_PM */ diff --git a/arch/ppc/platforms/4xx/bubinga.c b/arch/ppc/platforms/4xx/bubinga.c new file mode 100644 index 000000000..3678abf86 --- /dev/null +++ b/arch/ppc/platforms/4xx/bubinga.c @@ -0,0 +1,263 @@ +/* + * Support for IBM PPC 405EP evaluation board (Bubinga). + * + * Author: SAW (IBM), derived from walnut.c. + * Maintained by MontaVista Software + * + * 2003 (c) MontaVista Softare Inc. This file is licensed under the + * terms of the GNU General Public License version 2. This program is + * licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#undef DEBUG + +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + +extern bd_t __res; + +void *bubinga_rtc_base; + +/* Some IRQs unique to the board + * Used by the generic 405 PCI setup functions in ppc4xx_pci.c + */ +int __init +ppc405_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + static char pci_irq_table[][4] = + /* + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + {28, 28, 28, 28}, /* IDSEL 1 - PCI slot 1 */ + {29, 29, 29, 29}, /* IDSEL 2 - PCI slot 2 */ + {30, 30, 30, 30}, /* IDSEL 3 - PCI slot 3 */ + {31, 31, 31, 31}, /* IDSEL 4 - PCI slot 4 */ + }; + + const long min_idsel = 1, max_idsel = 4, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; +}; + +/* The serial clock for the chip is an internal clock determined by + * different clock speeds/dividers. + * Calculate the proper input baud rate and setup the serial driver. + */ +static void __init +bubinga_early_serial_map(void) +{ + u32 uart_div; + int uart_clock; + struct uart_port port; + + /* Calculate the serial clock input frequency + * + * The base baud is the PLL OUTA (provided in the board info + * structure) divided by the external UART Divisor, divided + * by 16. + */ + uart_div = (mfdcr(DCRN_CPC0_UCR_BASE) & DCRN_CPC0_UCR_U0DIV); + uart_clock = __res.bi_pllouta_freq / uart_div; + + /* Setup serial port access */ + memset(&port, 0, sizeof(port)); + port.membase = (void*)ACTING_UART0_IO_BASE; + port.irq = ACTING_UART0_INT; + port.uartclk = uart_clock; + port.regshift = 0; + port.iotype = SERIAL_IO_MEM; + port.flags = ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST; + port.line = 0; + + if (early_serial_setup(&port) != 0) { + printk("Early serial init of port 0 failed\n"); + } + + port.membase = (void*)ACTING_UART1_IO_BASE; + port.irq = ACTING_UART1_INT; + port.line = 1; + + if (early_serial_setup(&port) != 0) { + printk("Early serial init of port 1 failed\n"); + } +} + +void __init +bios_fixup(struct pci_controller *hose, struct pcil0_regs *pcip) +{ + + unsigned int bar_response, bar; + /* + * Expected PCI mapping: + * + * PLB addr PCI memory addr + * --------------------- --------------------- + * 0000'0000 - 7fff'ffff <--- 0000'0000 - 7fff'ffff + * 8000'0000 - Bfff'ffff ---> 8000'0000 - Bfff'ffff + * + * PLB addr PCI io addr + * --------------------- --------------------- + * e800'0000 - e800'ffff ---> 0000'0000 - 0001'0000 + * + * The following code is simplified by assuming that the bootrom + * has been well behaved in following this mapping. + */ + +#ifdef DEBUG + int i; + + printk("ioremap PCLIO_BASE = 0x%x\n", pcip); + printk("PCI bridge regs before fixup \n"); + for (i = 0; i <= 3; i++) { + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].ma))); + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].la))); + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].pcila))); + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].pciha))); + } + printk(" ptm1ms\t0x%x\n", in_le32(&(pcip->ptm1ms))); + printk(" ptm1la\t0x%x\n", in_le32(&(pcip->ptm1la))); + printk(" ptm2ms\t0x%x\n", in_le32(&(pcip->ptm2ms))); + printk(" ptm2la\t0x%x\n", in_le32(&(pcip->ptm2la))); + +#endif + + /* added for IBM boot rom version 1.15 bios bar changes -AK */ + + /* Disable region first */ + out_le32((void *) &(pcip->pmm[0].ma), 0x00000000); + /* PLB starting addr, PCI: 0x80000000 */ + out_le32((void *) &(pcip->pmm[0].la), 0x80000000); + /* PCI start addr, 0x80000000 */ + out_le32((void *) &(pcip->pmm[0].pcila), PPC405_PCI_MEM_BASE); + /* 512MB range of PLB to PCI */ + out_le32((void *) &(pcip->pmm[0].pciha), 0x00000000); + /* Enable no pre-fetch, enable region */ + out_le32((void *) &(pcip->pmm[0].ma), ((0xffffffff - + (PPC405_PCI_UPPER_MEM - + PPC405_PCI_MEM_BASE)) | 0x01)); + + /* Disable region one */ + out_le32((void *) &(pcip->pmm[1].ma), 0x00000000); + out_le32((void *) &(pcip->pmm[1].la), 0x00000000); + out_le32((void *) &(pcip->pmm[1].pcila), 0x00000000); + out_le32((void *) &(pcip->pmm[1].pciha), 0x00000000); + out_le32((void *) &(pcip->pmm[1].ma), 0x00000000); + out_le32((void *) &(pcip->ptm1ms), 0x00000001); + + /* Disable region two */ + out_le32((void *) &(pcip->pmm[2].ma), 0x00000000); + out_le32((void *) &(pcip->pmm[2].la), 0x00000000); + out_le32((void *) &(pcip->pmm[2].pcila), 0x00000000); + out_le32((void *) &(pcip->pmm[2].pciha), 0x00000000); + out_le32((void *) &(pcip->pmm[2].ma), 0x00000000); + out_le32((void *) &(pcip->ptm2ms), 0x00000000); + out_le32((void *) &(pcip->ptm2la), 0x00000000); + + /* Zero config bars */ + for (bar = PCI_BASE_ADDRESS_1; bar <= PCI_BASE_ADDRESS_2; bar += 4) { + early_write_config_dword(hose, hose->first_busno, + PCI_FUNC(hose->first_busno), bar, + 0x00000000); + early_read_config_dword(hose, hose->first_busno, + PCI_FUNC(hose->first_busno), bar, + &bar_response); + DBG("BUS %d, device %d, Function %d bar 0x%8.8x is 0x%8.8x\n", + hose->first_busno, PCI_SLOT(hose->first_busno), + PCI_FUNC(hose->first_busno), bar, bar_response); + } + /* end work arround */ + +#ifdef DEBUG + printk("PCI bridge regs after fixup \n"); + for (i = 0; i <= 3; i++) { + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].ma))); + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].la))); + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].pcila))); + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].pciha))); + } + printk(" ptm1ms\t0x%x\n", in_le32(&(pcip->ptm1ms))); + printk(" ptm1la\t0x%x\n", in_le32(&(pcip->ptm1la))); + printk(" ptm2ms\t0x%x\n", in_le32(&(pcip->ptm2ms))); + printk(" ptm2la\t0x%x\n", in_le32(&(pcip->ptm2la))); + +#endif +} + +void __init +bubinga_setup_arch(void) +{ + ppc4xx_setup_arch(); + + ibm_ocp_set_emac(0, 1); + + bubinga_early_serial_map(); + + /* RTC step for the evb405ep */ + bubinga_rtc_base = (void *) BUBINGA_RTC_VADDR; + TODC_INIT(TODC_TYPE_DS1743, bubinga_rtc_base, bubinga_rtc_base, + bubinga_rtc_base, 8); + /* Identify the system */ + printk("IBM Bubinga port (MontaVista Software, Inc. )\n"); +} + +void __init +bubinga_map_io(void) +{ + ppc4xx_map_io(); + io_block_mapping(BUBINGA_RTC_VADDR, + BUBINGA_RTC_PADDR, BUBINGA_RTC_SIZE, _PAGE_IO); +} + +void __init +platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + ppc4xx_init(r3, r4, r5, r6, r7); + + ppc_md.setup_arch = bubinga_setup_arch; + ppc_md.setup_io_mappings = bubinga_map_io; + +#ifdef CONFIG_GEN_RTC + ppc_md.time_init = todc_time_init; + ppc_md.set_rtc_time = todc_set_rtc_time; + ppc_md.get_rtc_time = todc_get_rtc_time; + ppc_md.nvram_read_val = todc_direct_read_val; + ppc_md.nvram_write_val = todc_direct_write_val; +#endif +#ifdef CONFIG_KGDB + ppc_md.early_serial_map = bubinga_early_serial_map; +#endif +} + diff --git a/arch/ppc/platforms/4xx/bubinga.h b/arch/ppc/platforms/4xx/bubinga.h new file mode 100644 index 000000000..b1df856f8 --- /dev/null +++ b/arch/ppc/platforms/4xx/bubinga.h @@ -0,0 +1,69 @@ +/* + * Support for IBM PPC 405EP evaluation board (Bubinga). + * + * Author: SAW (IBM), derived from walnut.h. + * Maintained by MontaVista Software + * + * 2003 (c) MontaVista Softare Inc. This file is licensed under the + * terms of the GNU General Public License version 2. This program is + * licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#ifdef __KERNEL__ +#ifndef __BUBINGA_H__ +#define __BUBINGA_H__ + +/* 405EP */ +#include + +#ifndef __ASSEMBLY__ +/* + * Data structure defining board information maintained by the boot + * ROM on IBM's evaluation board. An effort has been made to + * keep the field names consistent with the 8xx 'bd_t' board info + * structures. + */ + +typedef struct board_info { + unsigned char bi_s_version[4]; /* Version of this structure */ + unsigned char bi_r_version[30]; /* Version of the IBM ROM */ + unsigned int bi_memsize; /* DRAM installed, in bytes */ + unsigned char bi_enetaddr[2][6]; /* Local Ethernet MAC address */ unsigned char bi_pci_enetaddr[6]; /* PCI Ethernet MAC address */ + unsigned int bi_intfreq; /* Processor speed, in Hz */ + unsigned int bi_busfreq; /* PLB Bus speed, in Hz */ + unsigned int bi_pci_busfreq; /* PCI Bus speed, in Hz */ + unsigned int bi_opb_busfreq; /* OPB Bus speed, in Hz */ + unsigned int bi_pllouta_freq; /* PLL OUTA speed, in Hz */ +} bd_t; + +/* Some 4xx parts use a different timebase frequency from the internal clock. +*/ +#define bi_tbfreq bi_intfreq + + +/* Memory map for the Bubinga board. + * Generic 4xx plus RTC. + */ + +extern void *bubinga_rtc_base; +#define BUBINGA_RTC_PADDR ((uint)0xf0000000) +#define BUBINGA_RTC_VADDR BUBINGA_RTC_PADDR +#define BUBINGA_RTC_SIZE ((uint)8*1024) + +/* The UART clock is based off an internal clock - + * define BASE_BAUD based on the internal clock and divider(s). + * Since BASE_BAUD must be a constant, we will initialize it + * using clock/divider values which OpenBIOS initializes + * for typical configurations at various CPU speeds. + * The base baud is calculated as (FWDA / EXT UART DIV / 16) + */ +#define BASE_BAUD 0 + +#define BUBINGA_FPGA_BASE 0xF0300000 + +#define PPC4xx_MACHINE_NAME "IBM Bubinga" + +#endif /* !__ASSEMBLY__ */ +#endif /* __BUBINGA_H__ */ +#endif /* __KERNEL__ */ diff --git a/arch/ppc/platforms/4xx/ibm405ep.c b/arch/ppc/platforms/4xx/ibm405ep.c new file mode 100644 index 000000000..fb48e8254 --- /dev/null +++ b/arch/ppc/platforms/4xx/ibm405ep.c @@ -0,0 +1,134 @@ +/* + * arch/ppc/platforms/ibm405ep.c + * + * Support for IBM PPC 405EP processors. + * + * Author: SAW (IBM), derived from ibmnp405l.c. + * Maintained by MontaVista Software + * + * 2003 (c) MontaVista Softare Inc. This file is licensed under the + * terms of the GNU General Public License version 2. This program is + * licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +static struct ocp_func_mal_data ibm405ep_mal0_def = { + .num_tx_chans = 4, /* Number of TX channels */ + .num_rx_chans = 2, /* Number of RX channels */ + .txeob_irq = 11, /* TX End Of Buffer IRQ */ + .rxeob_irq = 12, /* RX End Of Buffer IRQ */ + .txde_irq = 13, /* TX Descriptor Error IRQ */ + .rxde_irq = 14, /* RX Descriptor Error IRQ */ + .serr_irq = 10, /* MAL System Error IRQ */ +}; +OCP_SYSFS_MAL_DATA() + +static struct ocp_func_emac_data ibm405ep_emac0_def = { + .rgmii_idx = -1, /* No RGMII */ + .rgmii_mux = -1, /* No RGMII */ + .zmii_idx = -1, /* ZMII device index */ + .zmii_mux = 0, /* ZMII input of this EMAC */ + .mal_idx = 0, /* MAL device index */ + .mal_rx_chan = 0, /* MAL rx channel number */ + .mal_tx_chan = 0, /* MAL tx channel number */ + .wol_irq = 9, /* WOL interrupt number */ + .mdio_idx = 0, /* MDIO via EMAC0 */ + .tah_idx = -1, /* No TAH */ +}; + +static struct ocp_func_emac_data ibm405ep_emac1_def = { + .rgmii_idx = -1, /* No RGMII */ + .rgmii_mux = -1, /* No RGMII */ + .zmii_idx = -1, /* ZMII device index */ + .zmii_mux = 0, /* ZMII input of this EMAC */ + .mal_idx = 0, /* MAL device index */ + .mal_rx_chan = 1, /* MAL rx channel number */ + .mal_tx_chan = 2, /* MAL tx channel number */ + .wol_irq = 9, /* WOL interrupt number */ + .mdio_idx = 0, /* MDIO via EMAC0 */ + .tah_idx = -1, /* No TAH */ +}; +OCP_SYSFS_EMAC_DATA() + +static struct ocp_func_iic_data ibm405ep_iic0_def = { + .fast_mode = 0, /* Use standad mode (100Khz) */ +}; +OCP_SYSFS_IIC_DATA() + +struct ocp_def core_ocp[] = { + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_OPB, + .index = 0, + .paddr = 0xEF600000, + .irq = OCP_IRQ_NA, + .pm = OCP_CPM_NA, + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_16550, + .index = 0, + .paddr = UART0_IO_BASE, + .irq = UART0_INT, + .pm = IBM_CPM_UART0 + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_16550, + .index = 1, + .paddr = UART1_IO_BASE, + .irq = UART1_INT, + .pm = IBM_CPM_UART1 + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_IIC, + .paddr = 0xEF600500, + .irq = 2, + .pm = IBM_CPM_IIC0, + .additions = &ibm405ep_iic0_def, + .show = &ocp_show_iic_data + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_GPIO, + .paddr = 0xEF600700, + .irq = OCP_IRQ_NA, + .pm = IBM_CPM_GPIO0 + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_MAL, + .paddr = OCP_PADDR_NA, + .irq = OCP_IRQ_NA, + .pm = OCP_CPM_NA, + .additions = &ibm405ep_mal0_def, + .show = &ocp_show_mal_data + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_EMAC, + .index = 0, + .paddr = EMAC0_BASE, + .irq = 15, + .pm = OCP_CPM_NA, + .additions = &ibm405ep_emac0_def, + .show = &ocp_show_emac_data + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_EMAC, + .index = 1, + .paddr = 0xEF600900, + .irq = 17, + .pm = OCP_CPM_NA, + .additions = &ibm405ep_emac1_def, + .show = &ocp_show_emac_data + }, + { .vendor = OCP_VENDOR_INVALID + } +}; diff --git a/arch/ppc/platforms/4xx/ibm405ep.h b/arch/ppc/platforms/4xx/ibm405ep.h new file mode 100644 index 000000000..e051e3fe8 --- /dev/null +++ b/arch/ppc/platforms/4xx/ibm405ep.h @@ -0,0 +1,148 @@ +/* + * arch/ppc/platforms/4xx/ibm405ep.h + * + * IBM PPC 405EP processor defines. + * + * Author: SAW (IBM), derived from ibm405gp.h. + * Maintained by MontaVista Software + * + * 2003 (c) MontaVista Softare Inc. This file is licensed under the + * terms of the GNU General Public License version 2. This program is + * licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#ifdef __KERNEL__ +#ifndef __ASM_IBM405EP_H__ +#define __ASM_IBM405EP_H__ + +#include + +/* ibm405.h at bottom of this file */ + +/* PCI + * PCI Bridge config reg definitions + * see 17-19 of manual + */ + +#define PPC405_PCI_CONFIG_ADDR 0xeec00000 +#define PPC405_PCI_CONFIG_DATA 0xeec00004 + +#define PPC405_PCI_PHY_MEM_BASE 0x80000000 /* hose_a->pci_mem_offset */ + /* setbat */ +#define PPC405_PCI_MEM_BASE PPC405_PCI_PHY_MEM_BASE /* setbat */ +#define PPC405_PCI_PHY_IO_BASE 0xe8000000 /* setbat */ +#define PPC405_PCI_IO_BASE PPC405_PCI_PHY_IO_BASE /* setbat */ + +#define PPC405_PCI_LOWER_MEM 0x80000000 /* hose_a->mem_space.start */ +#define PPC405_PCI_UPPER_MEM 0xBfffffff /* hose_a->mem_space.end */ +#define PPC405_PCI_LOWER_IO 0x00000000 /* hose_a->io_space.start */ +#define PPC405_PCI_UPPER_IO 0x0000ffff /* hose_a->io_space.end */ + +#define PPC405_ISA_IO_BASE PPC405_PCI_IO_BASE + +#define PPC4xx_PCI_IO_PADDR ((uint)PPC405_PCI_PHY_IO_BASE) +#define PPC4xx_PCI_IO_VADDR PPC4xx_PCI_IO_PADDR +#define PPC4xx_PCI_IO_SIZE ((uint)64*1024) +#define PPC4xx_PCI_CFG_PADDR ((uint)PPC405_PCI_CONFIG_ADDR) +#define PPC4xx_PCI_CFG_VADDR PPC4xx_PCI_CFG_PADDR +#define PPC4xx_PCI_CFG_SIZE ((uint)4*1024) +#define PPC4xx_PCI_LCFG_PADDR ((uint)0xef400000) +#define PPC4xx_PCI_LCFG_VADDR PPC4xx_PCI_LCFG_PADDR +#define PPC4xx_PCI_LCFG_SIZE ((uint)4*1024) +#define PPC4xx_ONB_IO_PADDR ((uint)0xef600000) +#define PPC4xx_ONB_IO_VADDR PPC4xx_ONB_IO_PADDR +#define PPC4xx_ONB_IO_SIZE ((uint)4*1024) + +/* serial port defines */ +#define RS_TABLE_SIZE 2 + +#define UART0_INT 0 +#define UART1_INT 1 + +#define PCIL0_BASE 0xEF400000 +#define UART0_IO_BASE 0xEF600300 +#define UART1_IO_BASE 0xEF600400 +#define EMAC0_BASE 0xEF600800 + +#define BD_EMAC_ADDR(e,i) bi_enetaddr[e][i] + +#if defined(CONFIG_UART0_TTYS0) +#define ACTING_UART0_IO_BASE UART0_IO_BASE +#define ACTING_UART1_IO_BASE UART1_IO_BASE +#define ACTING_UART0_INT UART0_INT +#define ACTING_UART1_INT UART1_INT +#else +#define ACTING_UART0_IO_BASE UART1_IO_BASE +#define ACTING_UART1_IO_BASE UART0_IO_BASE +#define ACTING_UART0_INT UART1_INT +#define ACTING_UART1_INT UART0_INT +#endif + +#define STD_UART_OP(num) \ + { 0, BASE_BAUD, 0, ACTING_UART##num##_INT, \ + (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST), \ + iomem_base: (u8 *)ACTING_UART##num##_IO_BASE, \ + io_type: SERIAL_IO_MEM}, + +#define SERIAL_DEBUG_IO_BASE ACTING_UART0_IO_BASE +#define SERIAL_PORT_DFNS \ + STD_UART_OP(0) \ + STD_UART_OP(1) + +/* DCR defines */ +#define DCRN_CPMSR_BASE 0x0BA +#define DCRN_CPMFR_BASE 0x0B9 + +#define DCRN_CPC0_PLLMR0_BASE 0x0F0 +#define DCRN_CPC0_BOOT_BASE 0x0F1 +#define DCRN_CPC0_CR1_BASE 0x0F2 +#define DCRN_CPC0_EPRCSR_BASE 0x0F3 +#define DCRN_CPC0_PLLMR1_BASE 0x0F4 +#define DCRN_CPC0_UCR_BASE 0x0F5 +#define DCRN_CPC0_UCR_U0DIV 0x07F +#define DCRN_CPC0_SRR_BASE 0x0F6 +#define DCRN_CPC0_JTAGID_BASE 0x0F7 +#define DCRN_CPC0_SPARE_BASE 0x0F8 +#define DCRN_CPC0_PCI_BASE 0x0F9 + + +#define IBM_CPM_GPT 0x80000000 /* GPT interface */ +#define IBM_CPM_PCI 0x40000000 /* PCI bridge */ +#define IBM_CPM_UIC 0x00010000 /* Universal Int Controller */ +#define IBM_CPM_CPU 0x00008000 /* processor core */ +#define IBM_CPM_EBC 0x00002000 /* EBC controller */ +#define IBM_CPM_SDRAM0 0x00004000 /* SDRAM memory controller */ +#define IBM_CPM_GPIO0 0x00001000 /* General Purpose IO */ +#define IBM_CPM_TMRCLK 0x00000400 /* CPU timers */ +#define IBM_CPM_PLB 0x00000100 /* PLB bus arbiter */ +#define IBM_CPM_OPB 0x00000080 /* PLB to OPB bridge */ +#define IBM_CPM_DMA 0x00000040 /* DMA controller */ +#define IBM_CPM_IIC0 0x00000010 /* IIC interface */ +#define IBM_CPM_UART1 0x00000002 /* serial port 0 */ +#define IBM_CPM_UART0 0x00000001 /* serial port 1 */ +#define DFLT_IBM4xx_PM ~(IBM_CPM_PCI | IBM_CPM_CPU | IBM_CPM_DMA \ + | IBM_CPM_OPB | IBM_CPM_EBC \ + | IBM_CPM_SDRAM0 | IBM_CPM_PLB \ + | IBM_CPM_UIC | IBM_CPM_TMRCLK) +#define DCRN_DMA0_BASE 0x100 +#define DCRN_DMA1_BASE 0x108 +#define DCRN_DMA2_BASE 0x110 +#define DCRN_DMA3_BASE 0x118 +#define DCRNCAP_DMA_SG 1 /* have DMA scatter/gather capability */ +#define DCRN_DMASR_BASE 0x120 +#define DCRN_EBC_BASE 0x012 +#define DCRN_DCP0_BASE 0x014 +#define DCRN_MAL_BASE 0x180 +#define DCRN_OCM0_BASE 0x018 +#define DCRN_PLB0_BASE 0x084 +#define DCRN_PLLMR_BASE 0x0B0 +#define DCRN_POB0_BASE 0x0A0 +#define DCRN_SDRAM0_BASE 0x010 +#define DCRN_UIC0_BASE 0x0C0 +#define UIC0 DCRN_UIC0_BASE + +#include + +#endif /* __ASM_IBM405EP_H__ */ +#endif /* __KERNEL__ */ diff --git a/arch/ppc/platforms/sbc82xx.c b/arch/ppc/platforms/sbc82xx.c new file mode 100644 index 000000000..0da699d3b --- /dev/null +++ b/arch/ppc/platforms/sbc82xx.c @@ -0,0 +1,113 @@ +/* + * arch/ppc/platforms/sbc82xx.c + * + * SBC82XX platform support + * + * Author: Guy Streeter + * + * Derived from: est8260_setup.c by Allen Curtis, ONZ + * + * Copyright 2004 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +static void (*callback_setup_arch)(void); + +extern unsigned char __res[sizeof(bd_t)]; + +extern void m8260_init(unsigned long r3, unsigned long r4, + unsigned long r5, unsigned long r6, unsigned long r7); + +extern void (*late_time_init)(void); + +static int +sbc82xx_show_cpuinfo(struct seq_file *m) +{ + bd_t *binfo = (bd_t *)__res; + + seq_printf(m, "vendor\t\t: Wind River\n" + "machine\t\t: SBC PowerQUICC II\n" + "\n" + "mem size\t\t: 0x%08lx\n" + "console baud\t\t: %ld\n" + "\n", + binfo->bi_memsize, + binfo->bi_baudrate); + return 0; +} + +static void __init +sbc82xx_setup_arch(void) +{ + printk("SBC PowerQUICC II Port\n"); + callback_setup_arch(); +} + +TODC_ALLOC(); + +/* + * Timer init happens before mem_init but after paging init, so we cannot + * directly use ioremap() at that time. + * late_time_init() is call after paging init. + */ +#ifdef CONFIG_GEN_RTC +static void sbc82xx_time_init(void) +{ + volatile memctl8260_t *mc = &immr->im_memctl; + TODC_INIT(TODC_TYPE_MK48T59, 0, 0, SBC82xx_TODC_NVRAM_ADDR, 0); + + /* Set up CS11 for RTC chip */ + mc->memc_br11=0; + mc->memc_or11=0xffff0836; + mc->memc_br11=0x80000801; + + todc_info->nvram_data = + (unsigned int)ioremap(todc_info->nvram_data, 0x2000); + BUG_ON(!todc_info->nvram_data); + ppc_md.get_rtc_time = todc_get_rtc_time; + ppc_md.set_rtc_time = todc_set_rtc_time; + ppc_md.nvram_read_val = todc_direct_read_val; + ppc_md.nvram_write_val = todc_direct_write_val; + todc_time_init(); +} +#endif /* CONFIG_GEN_RTC */ + +void __init +platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + /* Generic 8260 platform initialization */ + m8260_init(r3, r4, r5, r6, r7); + + /* u-boot may be using one of the FCC Ethernet devices. + Use the MAC address to the SCC. */ + __res[offsetof(bd_t, bi_enetaddr[5])] &= ~3; + + /* Anything special for this platform */ + ppc_md.show_cpuinfo = sbc82xx_show_cpuinfo; + + callback_setup_arch = ppc_md.setup_arch; + ppc_md.setup_arch = sbc82xx_setup_arch; +#ifdef CONFIG_GEN_RTC + ppc_md.time_init = NULL; + ppc_md.get_rtc_time = NULL; + ppc_md.set_rtc_time = NULL; + ppc_md.nvram_read_val = NULL; + ppc_md.nvram_write_val = NULL; + late_time_init = sbc82xx_time_init; +#endif /* CONFIG_GEN_RTC */ +} diff --git a/arch/ppc/platforms/sbc82xx.h b/arch/ppc/platforms/sbc82xx.h new file mode 100644 index 000000000..b9d1c8ddb --- /dev/null +++ b/arch/ppc/platforms/sbc82xx.h @@ -0,0 +1,24 @@ +/* Board information for the SBCPowerQUICCII, which should be generic for + * all 8260 boards. The IMMR is now given to us so the hard define + * will soon be removed. All of the clock values are computed from + * the configuration SCMR and the Power-On-Reset word. + */ + +#ifndef __PPC_SBC82xx_H__ +#define __PPC_SBC82xx_H__ + +#include + +#define IMAP_ADDR 0xf0000000 +#define CPM_MAP_ADDR 0xf0000000 + +#define SBC82xx_TODC_NVRAM_ADDR 0x80000000 + +#define SBC82xx_MACADDR_NVRAM_FCC1 0x220000c9 /* JP6B */ +#define SBC82xx_MACADDR_NVRAM_SCC1 0x220000cf /* JP6A */ +#define SBC82xx_MACADDR_NVRAM_FCC2 0x220000d5 /* JP7A */ +#define SBC82xx_MACADDR_NVRAM_FCC3 0x220000db /* JP7B */ + +#define BOOTROM_RESTART_ADDR ((uint)0x40000104) + +#endif /* __PPC_SBC82xx_H__ */ diff --git a/arch/ppc/syslib/dcr.S b/arch/ppc/syslib/dcr.S new file mode 100644 index 000000000..895f10243 --- /dev/null +++ b/arch/ppc/syslib/dcr.S @@ -0,0 +1,41 @@ +/* + * arch/ppc/syslib/dcr.S + * + * "Indirect" DCR access + * + * Copyright (c) 2004 Eugene Surovegin + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include + +#define DCR_ACCESS_PROLOG(table) \ + rlwinm r3,r3,4,18,27; \ + lis r5,table@h; \ + ori r5,r5,table@l; \ + add r3,r3,r5; \ + mtctr r3; \ + bctr + +_GLOBAL(__mfdcr) + DCR_ACCESS_PROLOG(__mfdcr_table) + +_GLOBAL(__mtdcr) + DCR_ACCESS_PROLOG(__mtdcr_table) + +__mfdcr_table: + mfdcr r3,0; blr +__mtdcr_table: + mtdcr 0,r4; blr + +dcr = 1 + .rept 1023 + mfdcr r3,dcr; blr + mtdcr dcr,r4; blr + dcr = dcr + 1 + .endr diff --git a/arch/ppc/syslib/ibm440gx_common.c b/arch/ppc/syslib/ibm440gx_common.c new file mode 100644 index 000000000..5da7bca6b --- /dev/null +++ b/arch/ppc/syslib/ibm440gx_common.c @@ -0,0 +1,212 @@ +/* + * arch/ppc/kernel/ibm440gx_common.c + * + * PPC440GX system library + * + * Eugene Surovegin or + * Copyright (c) 2003 Zultys Technologies + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ +#include +#include +#include +#include +#include +#include + +/* + * Calculate 440GX clocks + */ +static inline u32 __fix_zero(u32 v, u32 def){ + return v ? v : def; +} + +void __init ibm440gx_get_clocks(struct ibm44x_clocks* p, unsigned int sys_clk, + unsigned int ser_clk) +{ + u32 pllc = CPR_READ(DCRN_CPR_PLLC); + u32 plld = CPR_READ(DCRN_CPR_PLLD); + u32 uart0 = SDR_READ(DCRN_SDR_UART0); + u32 uart1 = SDR_READ(DCRN_SDR_UART1); + + /* Dividers */ + u32 fbdv = __fix_zero((plld >> 24) & 0x1f, 32); + u32 fwdva = __fix_zero((plld >> 16) & 0xf, 16); + u32 fwdvb = __fix_zero((plld >> 8) & 7, 8); + u32 lfbdv = __fix_zero(plld & 0x3f, 64); + u32 pradv0 = __fix_zero((CPR_READ(DCRN_CPR_PRIMAD) >> 24) & 7, 8); + u32 prbdv0 = __fix_zero((CPR_READ(DCRN_CPR_PRIMBD) >> 24) & 7, 8); + u32 opbdv0 = __fix_zero((CPR_READ(DCRN_CPR_OPBD) >> 24) & 3, 4); + u32 perdv0 = __fix_zero((CPR_READ(DCRN_CPR_PERD) >> 24) & 3, 4); + + /* Input clocks for primary dividers */ + u32 clk_a, clk_b; + + if (pllc & 0x40000000){ + u32 m; + + /* Feedback path */ + switch ((pllc >> 24) & 7){ + case 0: + /* PLLOUTx */ + m = ((pllc & 0x20000000) ? fwdvb : fwdva) * lfbdv; + break; + case 1: + /* CPU */ + m = fwdva * pradv0; + break; + case 5: + /* PERClk */ + m = fwdvb * prbdv0 * opbdv0 * perdv0; + break; + default: + printk(KERN_EMERG "invalid PLL feedback source\n"); + goto bypass; + } + m *= fbdv; + p->vco = sys_clk * m; + clk_a = p->vco / fwdva; + clk_b = p->vco / fwdvb; + } + else { +bypass: + /* Bypass system PLL */ + p->vco = 0; + clk_a = clk_b = sys_clk; + } + + p->cpu = clk_a / pradv0; + p->plb = clk_b / prbdv0; + p->opb = p->plb / opbdv0; + p->ebc = p->opb / perdv0; + + /* UARTs clock */ + if (uart0 & 0x00800000) + p->uart0 = ser_clk; + else + p->uart0 = p->plb / __fix_zero(uart0 & 0xff, 256); + + if (uart1 & 0x00800000) + p->uart1 = ser_clk; + else + p->uart1 = p->plb / __fix_zero(uart1 & 0xff, 256); +} + +/* Enable L2 cache (call with IRQs disabled) */ +void __init ibm440gx_l2c_enable(void){ + u32 r; + + asm volatile ("sync" ::: "memory"); + + /* Disable SRAM */ + mtdcr(DCRN_SRAM0_DPC, mfdcr(DCRN_SRAM0_DPC) & ~SRAM_DPC_ENABLE); + mtdcr(DCRN_SRAM0_SB0CR, mfdcr(DCRN_SRAM0_SB0CR) & ~SRAM_SBCR_BU_MASK); + mtdcr(DCRN_SRAM0_SB1CR, mfdcr(DCRN_SRAM0_SB1CR) & ~SRAM_SBCR_BU_MASK); + mtdcr(DCRN_SRAM0_SB2CR, mfdcr(DCRN_SRAM0_SB2CR) & ~SRAM_SBCR_BU_MASK); + mtdcr(DCRN_SRAM0_SB3CR, mfdcr(DCRN_SRAM0_SB3CR) & ~SRAM_SBCR_BU_MASK); + + /* Enable L2_MODE without ICU/DCU */ + r = mfdcr(DCRN_L2C0_CFG) & ~(L2C_CFG_ICU | L2C_CFG_DCU | L2C_CFG_SS_MASK); + r |= L2C_CFG_L2M | L2C_CFG_SS_256; + mtdcr(DCRN_L2C0_CFG, r); + + mtdcr(DCRN_L2C0_ADDR, 0); + + /* Hardware Clear Command */ + mtdcr(DCRN_L2C0_CMD, L2C_CMD_HCC); + while (!(mfdcr(DCRN_L2C0_SR) & L2C_SR_CC)) ; + + /* Clear Cache Parity and Tag Errors */ + mtdcr(DCRN_L2C0_CMD, L2C_CMD_CCP | L2C_CMD_CTE); + + /* Enable 64G snoop region starting at 0 */ + r = mfdcr(DCRN_L2C0_SNP0) & ~(L2C_SNP_BA_MASK | L2C_SNP_SSR_MASK); + r |= L2C_SNP_SSR_32G | L2C_SNP_ESR; + mtdcr(DCRN_L2C0_SNP0, r); + + r = mfdcr(DCRN_L2C0_SNP1) & ~(L2C_SNP_BA_MASK | L2C_SNP_SSR_MASK); + r |= 0x80000000 | L2C_SNP_SSR_32G | L2C_SNP_ESR; + mtdcr(DCRN_L2C0_SNP1, r); + + asm volatile ("sync" ::: "memory"); + + /* Enable ICU/DCU ports */ + r = mfdcr(DCRN_L2C0_CFG); + r &= ~(L2C_CFG_DCW_MASK | L2C_CFG_CPIM | L2C_CFG_TPIM | L2C_CFG_LIM + | L2C_CFG_PMUX_MASK | L2C_CFG_PMIM | L2C_CFG_TPEI | L2C_CFG_CPEI + | L2C_CFG_NAM | L2C_CFG_NBRM); + r |= L2C_CFG_ICU | L2C_CFG_DCU | L2C_CFG_TPC | L2C_CFG_CPC | L2C_CFG_FRAN + | L2C_CFG_SMCM; + mtdcr(DCRN_L2C0_CFG, r); + + asm volatile ("sync; isync" ::: "memory"); +} + +/* Disable L2 cache (call with IRQs disabled) */ +void __init ibm440gx_l2c_disable(void){ + u32 r; + + asm volatile ("sync" ::: "memory"); + + /* Disable L2C mode */ + r = mfdcr(DCRN_L2C0_CFG) & ~(L2C_CFG_L2M | L2C_CFG_ICU | L2C_CFG_DCU); + mtdcr(DCRN_L2C0_CFG, r); + + /* Enable SRAM */ + mtdcr(DCRN_SRAM0_DPC, mfdcr(DCRN_SRAM0_DPC) | SRAM_DPC_ENABLE); + mtdcr(DCRN_SRAM0_SB0CR, + SRAM_SBCR_BAS0 | SRAM_SBCR_BS_64KB | SRAM_SBCR_BU_RW); + mtdcr(DCRN_SRAM0_SB1CR, + SRAM_SBCR_BAS1 | SRAM_SBCR_BS_64KB | SRAM_SBCR_BU_RW); + mtdcr(DCRN_SRAM0_SB2CR, + SRAM_SBCR_BAS2 | SRAM_SBCR_BS_64KB | SRAM_SBCR_BU_RW); + mtdcr(DCRN_SRAM0_SB3CR, + SRAM_SBCR_BAS3 | SRAM_SBCR_BS_64KB | SRAM_SBCR_BU_RW); + + asm volatile ("sync; isync" ::: "memory"); +} + +int __init ibm440gx_get_eth_grp(void) +{ + return (SDR_READ(DCRN_SDR_PFC1) & DCRN_SDR_PFC1_EPS) >> DCRN_SDR_PFC1_EPS_SHIFT; +} + +void __init ibm440gx_set_eth_grp(int group) +{ + SDR_WRITE(DCRN_SDR_PFC1, (SDR_READ(DCRN_SDR_PFC1) & ~DCRN_SDR_PFC1_EPS) | (group << DCRN_SDR_PFC1_EPS_SHIFT)); +} + +void __init ibm440gx_tah_enable(void) +{ + /* Enable TAH0 and TAH1 */ + SDR_WRITE(DCRN_SDR_MFR,SDR_READ(DCRN_SDR_MFR) & + ~DCRN_SDR_MFR_TAH0); + SDR_WRITE(DCRN_SDR_MFR,SDR_READ(DCRN_SDR_MFR) & + ~DCRN_SDR_MFR_TAH1); +} + +int ibm440gx_show_cpuinfo(struct seq_file *m){ + + u32 l2c_cfg = mfdcr(DCRN_L2C0_CFG); + const char* s; + if (l2c_cfg & L2C_CFG_L2M){ + switch (l2c_cfg & (L2C_CFG_ICU | L2C_CFG_DCU)){ + case L2C_CFG_ICU: s = "I-Cache only"; break; + case L2C_CFG_DCU: s = "D-Cache only"; break; + default: s = "I-Cache/D-Cache"; break; + } + } + else + s = "disabled"; + + seq_printf(m, "L2-Cache\t: %s (0x%08x 0x%08x)\n", s, + l2c_cfg, mfdcr(DCRN_L2C0_SR)); + + return 0; +} + diff --git a/arch/ppc/syslib/ibm440gx_common.h b/arch/ppc/syslib/ibm440gx_common.h new file mode 100644 index 000000000..5dbca9896 --- /dev/null +++ b/arch/ppc/syslib/ibm440gx_common.h @@ -0,0 +1,54 @@ +/* + * arch/ppc/kernel/ibm440gx_common.h + * + * PPC440GX system library + * + * Eugene Surovegin or + * Copyright (c) 2003 Zultys Technologies + * + * 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. + * + */ +#ifdef __KERNEL__ +#ifndef __PPC_SYSLIB_IBM440GX_COMMON_H +#define __PPC_SYSLIB_IBM440GX_COMMON_H + +#ifndef __ASSEMBLY__ + +#include +#include +#include +#include + +/* + * Please, refer to the Figure 14.1 in 440GX user manual + * + * if internal UART clock is used, ser_clk is ignored + */ +void ibm440gx_get_clocks(struct ibm44x_clocks*, unsigned int sys_clk, + unsigned int ser_clk) __init; + +/* Enable L2 cache */ +void ibm440gx_l2c_enable(void) __init; + +/* Disable L2 cache */ +void ibm440gx_l2c_disable(void) __init; + +/* Get Ethernet Group */ +int ibm440gx_get_eth_grp(void) __init; + +/* Set Ethernet Group */ +void ibm440gx_set_eth_grp(int group) __init; + +/* Enable TAH devices */ +void ibm440gx_tah_enable(void) __init; + +/* Add L2C info to /proc/cpuinfo */ +int ibm440gx_show_cpuinfo(struct seq_file*); + +#endif /* __ASSEMBLY__ */ +#endif /* __PPC_SYSLIB_IBM440GX_COMMON_H */ +#endif /* __KERNEL__ */ diff --git a/arch/ppc/syslib/ibm44x_common.h b/arch/ppc/syslib/ibm44x_common.h new file mode 100644 index 000000000..ee1053ac2 --- /dev/null +++ b/arch/ppc/syslib/ibm44x_common.h @@ -0,0 +1,36 @@ +/* + * arch/ppc/kernel/ibm44x_common.h + * + * PPC44x system library + * + * Eugene Surovegin or + * Copyright (c) 2003 Zultys Technologies + * + * 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. + * + */ +#ifdef __KERNEL__ +#ifndef __PPC_SYSLIB_IBM44x_COMMON_H +#define __PPC_SYSLIB_IBM44x_COMMON_H + +#ifndef __ASSEMBLY__ + +/* + * All clocks are in Hz + */ +struct ibm44x_clocks { + unsigned int vco; /* VCO, 0 if system PLL is bypassed */ + unsigned int cpu; /* CPUCoreClk */ + unsigned int plb; /* PLBClk */ + unsigned int opb; /* OPBClk */ + unsigned int ebc; /* PerClk */ + unsigned int uart0; + unsigned int uart1; +}; + +#endif /* __ASSEMBLY__ */ +#endif /* __PPC_SYSLIB_IBM44x_COMMON_H */ +#endif /* __KERNEL__ */ diff --git a/arch/ppc/syslib/ocp.c b/arch/ppc/syslib/ocp.c new file mode 100644 index 000000000..a5156c517 --- /dev/null +++ b/arch/ppc/syslib/ocp.c @@ -0,0 +1,485 @@ +/* + * ocp.c + * + * (c) Benjamin Herrenschmidt (benh@kernel.crashing.org) + * Mipsys - France + * + * Derived from work (c) Armin Kuster akuster@pacbell.net + * + * Additional support and port to 2.6 LDM/sysfs by + * Matt Porter + * Copyright 2004 MontaVista Software, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * OCP (On Chip Peripheral) is a software emulated "bus" with a + * pseudo discovery method for dumb peripherals. Usually these type + * of peripherals are found on embedded SoC (System On a Chip) + * processors or highly integrated system controllers that have + * a host bridge and many peripherals. Common examples where + * this is already used include the PPC4xx, PPC85xx, MPC52xx, + * and MV64xxx parts. + * + * This subsystem creates a standard OCP bus type within the + * device model. The devices on the OCP bus are seeded by an + * an initial OCP device array created by the arch-specific + * Device entries can be added/removed/modified through OCP + * helper functions to accomodate system and board-specific + * parameters commonly found in embedded systems. OCP also + * provides a standard method for devices to describe extended + * attributes about themselves to the system. A standard access + * method allows OCP drivers to obtain the information, both + * SoC-specific and system/board-specific, needed for operation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +//#define DBG(x) printk x +#define DBG(x) + +extern int mem_init_done; + +extern struct ocp_def core_ocp[]; /* Static list of devices, provided by + CPU core */ + +LIST_HEAD(ocp_devices); /* List of all OCP devices */ +DECLARE_RWSEM(ocp_devices_sem); /* Global semaphores for those lists */ + +static int ocp_inited; + +/* Sysfs support */ +#define OCP_DEF_ATTR(field, format_string) \ +static ssize_t \ +show_##field(struct device *dev, char *buf) \ +{ \ + struct ocp_device *odev = to_ocp_dev(dev); \ + \ + return sprintf(buf, format_string, odev->def->field); \ +} \ +static DEVICE_ATTR(field, S_IRUGO, show_##field, NULL); + +OCP_DEF_ATTR(vendor, "0x%04x\n"); +OCP_DEF_ATTR(function, "0x%04x\n"); +OCP_DEF_ATTR(index, "0x%04x\n"); +#ifdef CONFIG_PTE_64BIT +OCP_DEF_ATTR(paddr, "0x%016Lx\n"); +#else +OCP_DEF_ATTR(paddr, "0x%08lx\n"); +#endif +OCP_DEF_ATTR(irq, "%d\n"); +OCP_DEF_ATTR(pm, "%lu\n"); + +void ocp_create_sysfs_dev_files(struct ocp_device *odev) +{ + struct device *dev = &odev->dev; + + /* Current OCP device def attributes */ + device_create_file(dev, &dev_attr_vendor); + device_create_file(dev, &dev_attr_function); + device_create_file(dev, &dev_attr_index); + device_create_file(dev, &dev_attr_paddr); + device_create_file(dev, &dev_attr_irq); + device_create_file(dev, &dev_attr_pm); + /* Current OCP device additions attributes */ + if (odev->def->additions && odev->def->show) + odev->def->show(dev); +} + +/** + * ocp_device_match - Match one driver to one device + * @drv: driver to match + * @dev: device to match + * + * This function returns 0 if the driver and device don't match + */ +static int +ocp_device_match(struct device *dev, struct device_driver *drv) +{ + struct ocp_device *ocp_dev = to_ocp_dev(dev); + struct ocp_driver *ocp_drv = to_ocp_drv(drv); + const struct ocp_device_id *ids = ocp_drv->id_table; + + if (!ids) + return 0; + + while (ids->vendor || ids->function) { + if ((ids->vendor == OCP_ANY_ID + || ids->vendor == ocp_dev->def->vendor) + && (ids->function == OCP_ANY_ID + || ids->function == ocp_dev->def->function)) + return 1; + ids++; + } + return 0; +} + +static int +ocp_device_probe(struct device *dev) +{ + int error = 0; + struct ocp_driver *drv; + struct ocp_device *ocp_dev; + + drv = to_ocp_drv(dev->driver); + ocp_dev = to_ocp_dev(dev); + + if (drv->probe) { + error = drv->probe(ocp_dev); + if (error >= 0) { + ocp_dev->driver = drv; + error = 0; + } + } + return error; +} + +static int +ocp_device_remove(struct device *dev) +{ + struct ocp_device *ocp_dev = to_ocp_dev(dev); + + if (ocp_dev->driver) { + if (ocp_dev->driver->remove) + ocp_dev->driver->remove(ocp_dev); + ocp_dev->driver = NULL; + } + return 0; +} + +static int +ocp_device_suspend(struct device *dev, u32 state) +{ + struct ocp_device *ocp_dev = to_ocp_dev(dev); + struct ocp_driver *ocp_drv = to_ocp_drv(dev->driver); + + if (dev->driver && ocp_drv->suspend) + return ocp_drv->suspend(ocp_dev, state); + return 0; +} + +static int +ocp_device_resume(struct device *dev) +{ + struct ocp_device *ocp_dev = to_ocp_dev(dev); + struct ocp_driver *ocp_drv = to_ocp_drv(dev->driver); + + if (dev->driver && ocp_drv->resume) + return ocp_drv->resume(ocp_dev); + return 0; +} + +struct bus_type ocp_bus_type = { + .name = "ocp", + .match = ocp_device_match, + .suspend = ocp_device_suspend, + .resume = ocp_device_resume, +}; + +/** + * ocp_register_driver - Register an OCP driver + * @drv: pointer to statically defined ocp_driver structure + * + * The driver's probe() callback is called either recursively + * by this function or upon later call of ocp_driver_init + * + * NOTE: Detection of devices is a 2 pass step on this implementation, + * hotswap isn't supported. First, all OCP devices are put in the device + * list, _then_ all drivers are probed on each match. + */ +int +ocp_register_driver(struct ocp_driver *drv) +{ + /* initialize common driver fields */ + drv->driver.name = drv->name; + drv->driver.bus = &ocp_bus_type; + drv->driver.probe = ocp_device_probe; + drv->driver.remove = ocp_device_remove; + + /* register with core */ + return driver_register(&drv->driver); +} + +/** + * ocp_unregister_driver - Unregister an OCP driver + * @drv: pointer to statically defined ocp_driver structure + * + * The driver's remove() callback is called recursively + * by this function for any device already registered + */ +void +ocp_unregister_driver(struct ocp_driver *drv) +{ + DBG(("ocp: ocp_unregister_driver(%s)...\n", drv->name)); + + driver_unregister(&drv->driver); + + DBG(("ocp: ocp_unregister_driver(%s)... done.\n", drv->name)); +} + +/* Core of ocp_find_device(). Caller must hold ocp_devices_sem */ +static struct ocp_device * +__ocp_find_device(unsigned int vendor, unsigned int function, int index) +{ + struct list_head *entry; + struct ocp_device *dev, *found = NULL; + + DBG(("ocp: __ocp_find_device(vendor: %x, function: %x, index: %d)...\n", vendor, function, index)); + + list_for_each(entry, &ocp_devices) { + dev = list_entry(entry, struct ocp_device, link); + if (vendor != OCP_ANY_ID && vendor != dev->def->vendor) + continue; + if (function != OCP_ANY_ID && function != dev->def->function) + continue; + if (index != OCP_ANY_INDEX && index != dev->def->index) + continue; + found = dev; + break; + } + + DBG(("ocp: __ocp_find_device(vendor: %x, function: %x, index: %d)... done\n", vendor, function, index)); + + return found; +} + +/** + * ocp_find_device - Find a device by function & index + * @vendor: vendor ID of the device (or OCP_ANY_ID) + * @function: function code of the device (or OCP_ANY_ID) + * @idx: index of the device (or OCP_ANY_INDEX) + * + * This function allows a lookup of a given function by it's + * index, it's typically used to find the MAL or ZMII associated + * with an EMAC or similar horrors. + * You can pass vendor, though you usually want OCP_ANY_ID there... + */ +struct ocp_device * +ocp_find_device(unsigned int vendor, unsigned int function, int index) +{ + struct ocp_device *dev; + + down_read(&ocp_devices_sem); + dev = __ocp_find_device(vendor, function, index); + up_read(&ocp_devices_sem); + + return dev; +} + +/** + * ocp_get_one_device - Find a def by function & index + * @vendor: vendor ID of the device (or OCP_ANY_ID) + * @function: function code of the device (or OCP_ANY_ID) + * @idx: index of the device (or OCP_ANY_INDEX) + * + * This function allows a lookup of a given ocp_def by it's + * vendor, function, and index. The main purpose for is to + * allow modification of the def before binding to the driver + */ +struct ocp_def * +ocp_get_one_device(unsigned int vendor, unsigned int function, int index) +{ + struct ocp_device *dev; + struct ocp_def *found = NULL; + + DBG(("ocp: ocp_get_one_device(vendor: %x, function: %x, index: %d)...\n", + vendor, function, index)); + + dev = ocp_find_device(vendor, function, index); + + if (dev) + found = dev->def; + + DBG(("ocp: ocp_get_one_device(vendor: %x, function: %x, index: %d)... done.\n", + vendor, function, index)); + + return found; +} + +/** + * ocp_add_one_device - Add a device + * @def: static device definition structure + * + * This function adds a device definition to the + * device list. It may only be called before + * ocp_driver_init() and will return an error + * otherwise. + */ +int +ocp_add_one_device(struct ocp_def *def) +{ + struct ocp_device *dev; + + DBG(("ocp: ocp_add_one_device()...\n")); + + /* Can't be called after ocp driver init */ + if (ocp_inited) + return 1; + + if (mem_init_done) + dev = kmalloc(sizeof(*dev), GFP_KERNEL); + else + dev = alloc_bootmem(sizeof(*dev)); + + if (dev == NULL) + return 1; + memset(dev, 0, sizeof(*dev)); + dev->def = def; + dev->current_state = 4; + sprintf(dev->name, "OCP device %04x:%04x:%04x", + dev->def->vendor, dev->def->function, dev->def->index); + down_write(&ocp_devices_sem); + list_add_tail(&dev->link, &ocp_devices); + up_write(&ocp_devices_sem); + + DBG(("ocp: ocp_add_one_device()...done\n")); + + return 0; +} + +/** + * ocp_remove_one_device - Remove a device by function & index + * @vendor: vendor ID of the device (or OCP_ANY_ID) + * @function: function code of the device (or OCP_ANY_ID) + * @idx: index of the device (or OCP_ANY_INDEX) + * + * This function allows removal of a given function by its + * index. It may only be called before ocp_driver_init() + * and will return an error otherwise. + */ +int +ocp_remove_one_device(unsigned int vendor, unsigned int function, int index) +{ + struct ocp_device *dev; + + DBG(("ocp: ocp_remove_one_device(vendor: %x, function: %x, index: %d)...\n", vendor, function, index)); + + /* Can't be called after ocp driver init */ + if (ocp_inited) + return 1; + + down_write(&ocp_devices_sem); + dev = __ocp_find_device(vendor, function, index); + list_del((struct list_head *)dev); + up_write(&ocp_devices_sem); + + DBG(("ocp: ocp_remove_one_device(vendor: %x, function: %x, index: %d)... done.\n", vendor, function, index)); + + return 0; +} + +/** + * ocp_for_each_device - Iterate over OCP devices + * @callback: routine to execute for each ocp device. + * @arg: user data to be passed to callback routine. + * + * This routine holds the ocp_device semaphore, so the + * callback routine cannot modify the ocp_device list. + */ +void +ocp_for_each_device(void(*callback)(struct ocp_device *, void *arg), void *arg) +{ + struct list_head *entry; + + if (callback) { + down_read(&ocp_devices_sem); + list_for_each(entry, &ocp_devices) + callback(list_entry(entry, struct ocp_device, link), + arg); + up_read(&ocp_devices_sem); + } +} + +/** + * ocp_early_init - Init OCP device management + * + * This function builds the list of devices before setup_arch. + * This allows platform code to modify the device lists before + * they are bound to drivers (changes to paddr, removing devices + * etc) + */ +int __init +ocp_early_init(void) +{ + struct ocp_def *def; + + DBG(("ocp: ocp_early_init()...\n")); + + /* Fill the devices list */ + for (def = core_ocp; def->vendor != OCP_VENDOR_INVALID; def++) + ocp_add_one_device(def); + + DBG(("ocp: ocp_early_init()... done.\n")); + + return 0; +} + +/** + * ocp_driver_init - Init OCP device management + * + * This function is meant to be called via OCP bus registration. + */ +static int __init +ocp_driver_init(void) +{ + int ret = 0, index = 0; + struct device *ocp_bus; + struct list_head *entry; + struct ocp_device *dev; + + if (ocp_inited) + return ret; + ocp_inited = 1; + + DBG(("ocp: ocp_driver_init()...\n")); + + /* Allocate/register primary OCP bus */ + ocp_bus = kmalloc(sizeof(struct device), GFP_KERNEL); + if (ocp_bus == NULL) + return 1; + memset(ocp_bus, 0, sizeof(struct device)); + strcpy(ocp_bus->bus_id, "ocp"); + + bus_register(&ocp_bus_type); + + device_register(ocp_bus); + + /* Put each OCP device into global device list */ + list_for_each(entry, &ocp_devices) { + dev = list_entry(entry, struct ocp_device, link); + sprintf(dev->dev.bus_id, "%2.2x", index); + dev->dev.parent = ocp_bus; + dev->dev.bus = &ocp_bus_type; + device_register(&dev->dev); + ocp_create_sysfs_dev_files(dev); + index++; + } + + DBG(("ocp: ocp_driver_init()... done.\n")); + + return 0; +} + +postcore_initcall(ocp_driver_init); + +EXPORT_SYMBOL(ocp_bus_type); +EXPORT_SYMBOL(ocp_find_device); +EXPORT_SYMBOL(ocp_register_driver); +EXPORT_SYMBOL(ocp_unregister_driver); diff --git a/arch/ppc64/lib/locks.c b/arch/ppc64/lib/locks.c new file mode 100644 index 000000000..4610cb938 --- /dev/null +++ b/arch/ppc64/lib/locks.c @@ -0,0 +1,285 @@ +/* + * Spin and read/write lock operations. + * + * Copyright (C) 2001-2004 Paul Mackerras , IBM + * Copyright (C) 2001 Anton Blanchard , IBM + * Copyright (C) 2002 Dave Engebretsen , IBM + * Rework to support virtual processors + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include + +#ifndef CONFIG_SPINLINE + +/* + * On a system with shared processors (that is, where a physical + * processor is multiplexed between several virtual processors), + * there is no point spinning on a lock if the holder of the lock + * isn't currently scheduled on a physical processor. Instead + * we detect this situation and ask the hypervisor to give the + * rest of our timeslice to the lock holder. + * + * So that we can tell which virtual processor is holding a lock, + * we put 0x80000000 | smp_processor_id() in the lock when it is + * held. Conveniently, we have a word in the paca that holds this + * value. + */ + +/* waiting for a spinlock... */ +#if defined(CONFIG_PPC_SPLPAR) || defined(CONFIG_PPC_ISERIES) +void __spin_yield(spinlock_t *lock) +{ + unsigned int lock_value, holder_cpu, yield_count; + struct paca_struct *holder_paca; + + lock_value = lock->lock; + if (lock_value == 0) + return; + holder_cpu = lock_value & 0xffff; + BUG_ON(holder_cpu >= NR_CPUS); + holder_paca = &paca[holder_cpu]; + yield_count = holder_paca->xLpPaca.xYieldCount; + if ((yield_count & 1) == 0) + return; /* virtual cpu is currently running */ + rmb(); + if (lock->lock != lock_value) + return; /* something has changed */ +#ifdef CONFIG_PPC_ISERIES + HvCall2(HvCallBaseYieldProcessor, HvCall_YieldToProc, + ((u64)holder_cpu << 32) | yield_count); +#else + plpar_hcall_norets(H_CONFER, holder_cpu, yield_count); +#endif +} + +#else /* SPLPAR || ISERIES */ +#define __spin_yield(x) barrier() +#endif + +/* + * This returns the old value in the lock, so we succeeded + * in getting the lock if the return value is 0. + */ +static __inline__ unsigned long __spin_trylock(spinlock_t *lock) +{ + unsigned long tmp, tmp2; + + __asm__ __volatile__( +" lwz %1,24(13) # __spin_trylock\n\ +1: lwarx %0,0,%2\n\ + cmpwi 0,%0,0\n\ + bne- 2f\n\ + stwcx. %1,0,%2\n\ + bne- 1b\n\ + isync\n\ +2:" : "=&r" (tmp), "=&r" (tmp2) + : "r" (&lock->lock) + : "cr0", "memory"); + + return tmp; +} + +int _raw_spin_trylock(spinlock_t *lock) +{ + return __spin_trylock(lock) == 0; +} + +EXPORT_SYMBOL(_raw_spin_trylock); + +void _raw_spin_lock(spinlock_t *lock) +{ + while (1) { + if (likely(__spin_trylock(lock) == 0)) + break; + do { + HMT_low(); + __spin_yield(lock); + } while (likely(lock->lock != 0)); + HMT_medium(); + } +} + +EXPORT_SYMBOL(_raw_spin_lock); + +void _raw_spin_lock_flags(spinlock_t *lock, unsigned long flags) +{ + unsigned long flags_dis; + + while (1) { + if (likely(__spin_trylock(lock) == 0)) + break; + local_save_flags(flags_dis); + local_irq_restore(flags); + do { + HMT_low(); + __spin_yield(lock); + } while (likely(lock->lock != 0)); + HMT_medium(); + local_irq_restore(flags_dis); + } +} + +EXPORT_SYMBOL(_raw_spin_lock_flags); + +void spin_unlock_wait(spinlock_t *lock) +{ + while (lock->lock) + __spin_yield(lock); +} + +EXPORT_SYMBOL(spin_unlock_wait); + +/* + * Waiting for a read lock or a write lock on a rwlock... + * This turns out to be the same for read and write locks, since + * we only know the holder if it is write-locked. + */ +#if defined(CONFIG_PPC_SPLPAR) || defined(CONFIG_PPC_ISERIES) +void __rw_yield(rwlock_t *rw) +{ + int lock_value; + unsigned int holder_cpu, yield_count; + struct paca_struct *holder_paca; + + lock_value = rw->lock; + if (lock_value >= 0) + return; /* no write lock at present */ + holder_cpu = lock_value & 0xffff; + BUG_ON(holder_cpu >= NR_CPUS); + holder_paca = &paca[holder_cpu]; + yield_count = holder_paca->xLpPaca.xYieldCount; + if ((yield_count & 1) == 0) + return; /* virtual cpu is currently running */ + rmb(); + if (rw->lock != lock_value) + return; /* something has changed */ +#ifdef CONFIG_PPC_ISERIES + HvCall2(HvCallBaseYieldProcessor, HvCall_YieldToProc, + ((u64)holder_cpu << 32) | yield_count); +#else + plpar_hcall_norets(H_CONFER, holder_cpu, yield_count); +#endif +} + +#else /* SPLPAR || ISERIES */ +#define __rw_yield(x) barrier() +#endif + +/* + * This returns the old value in the lock + 1, + * so we got a read lock if the return value is > 0. + */ +static __inline__ long __read_trylock(rwlock_t *rw) +{ + long tmp; + + __asm__ __volatile__( +"1: lwarx %0,0,%1 # read_trylock\n\ + extsw %0,%0\n\ + addic. %0,%0,1\n\ + ble- 2f\n\ + stwcx. %0,0,%1\n\ + bne- 1b\n\ + isync\n\ +2:" : "=&r" (tmp) + : "r" (&rw->lock) + : "cr0", "xer", "memory"); + + return tmp; +} + +int _raw_read_trylock(rwlock_t *rw) +{ + return __read_trylock(rw) > 0; +} + +EXPORT_SYMBOL(_raw_read_trylock); + +void _raw_read_lock(rwlock_t *rw) +{ + while (1) { + if (likely(__read_trylock(rw) > 0)) + break; + do { + HMT_low(); + __rw_yield(rw); + } while (likely(rw->lock < 0)); + HMT_medium(); + } +} + +EXPORT_SYMBOL(_raw_read_lock); + +void _raw_read_unlock(rwlock_t *rw) +{ + long tmp; + + __asm__ __volatile__( + "eieio # read_unlock\n\ +1: lwarx %0,0,%1\n\ + addic %0,%0,-1\n\ + stwcx. %0,0,%1\n\ + bne- 1b" + : "=&r"(tmp) + : "r"(&rw->lock) + : "cr0", "memory"); +} + +EXPORT_SYMBOL(_raw_read_unlock); + +/* + * This returns the old value in the lock, + * so we got the write lock if the return value is 0. + */ +static __inline__ long __write_trylock(rwlock_t *rw) +{ + long tmp, tmp2; + + __asm__ __volatile__( +" lwz %1,24(13) # write_trylock\n\ +1: lwarx %0,0,%2\n\ + cmpwi 0,%0,0\n\ + bne- 2f\n\ + stwcx. %1,0,%2\n\ + bne- 1b\n\ + isync\n\ +2:" : "=&r" (tmp), "=&r" (tmp2) + : "r" (&rw->lock) + : "cr0", "memory"); + + return tmp; +} + +int _raw_write_trylock(rwlock_t *rw) +{ + return __write_trylock(rw) == 0; +} + +EXPORT_SYMBOL(_raw_write_trylock); + +void _raw_write_lock(rwlock_t *rw) +{ + while (1) { + if (likely(__write_trylock(rw) == 0)) + break; + do { + HMT_low(); + __rw_yield(rw); + } while (likely(rw->lock != 0)); + HMT_medium(); + } +} + +EXPORT_SYMBOL(_raw_write_lock); + +#endif /* CONFIG_SPINLINE */ diff --git a/arch/s390/lib/memset.S b/arch/s390/lib/memset.S deleted file mode 100644 index 447af53f8..000000000 --- a/arch/s390/lib/memset.S +++ /dev/null @@ -1,30 +0,0 @@ -/* - * arch/s390/lib/memset.S - * S390 fast memset routine - * - * S390 version - * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation - * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), - */ - -/* - * R2 = address to memory area - * R3 = byte to fill memory with - * R4 = number of bytes to fill - */ - .globl memset -memset: - LTR 4,4 - JZ memset_end - LR 0,2 # save pointer to memory area - LR 1,3 # move pad byte to R1 - LR 3,4 - SR 4,4 # no source for MVCLE, only a pad byte - SR 5,5 - MVCLE 2,4,0(1) # thats it, MVCLE is your friend - JO .-4 - LR 2,0 # return pointer to mem. -memset_end: - BR 14 - - diff --git a/arch/s390/lib/memset64.S b/arch/s390/lib/memset64.S deleted file mode 100644 index 1e4b035d2..000000000 --- a/arch/s390/lib/memset64.S +++ /dev/null @@ -1,30 +0,0 @@ -/* - * arch/s390/lib/memset.S - * S390 fast memset routine - * - * S390 version - * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation - * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), - */ - -/* - * R2 = address to memory area - * R3 = byte to fill memory with - * R4 = number of bytes to fill - */ - .globl memset -memset: - LTGR 4,4 - JZ memset_end - LGR 0,2 # save pointer to memory area - LGR 1,3 # move pad byte to R1 - LGR 3,4 - SGR 4,4 # no source for MVCLE, only a pad byte - SGR 5,5 - MVCLE 2,4,0(1) # thats it, MVCLE is your friend - JO .-4 - LGR 2,0 # return pointer to mem. -memset_end: - BR 14 - - diff --git a/arch/s390/lib/strcmp.S b/arch/s390/lib/strcmp.S deleted file mode 100644 index 340edffb5..000000000 --- a/arch/s390/lib/strcmp.S +++ /dev/null @@ -1,27 +0,0 @@ -/* - * arch/s390/lib/strcmp.S - * S390 strcmp routine - * - * S390 version - * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation - * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), - */ - -/* - * R2 = address of compare string - * R3 = address of test string - */ - .globl strcmp -strcmp: - SR 0,0 - SR 1,1 - CLST 2,3 - JO .-4 - JE strcmp_equal - IC 0,0(3) - IC 1,0(2) - SR 1,0 -strcmp_equal: - LR 2,1 - BR 14 - diff --git a/arch/s390/lib/strcmp64.S b/arch/s390/lib/strcmp64.S deleted file mode 100644 index 124f3df26..000000000 --- a/arch/s390/lib/strcmp64.S +++ /dev/null @@ -1,27 +0,0 @@ -/* - * arch/s390/lib/strcmp.S - * S390 strcmp routine - * - * S390 version - * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation - * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), - */ - -/* - * R2 = address of compare string - * R3 = address of test string - */ - .globl strcmp -strcmp: - SGR 0,0 - SGR 1,1 - CLST 2,3 - JO .-4 - JE strcmp_equal - IC 0,0(3) - IC 1,0(2) - SGR 1,0 -strcmp_equal: - LGR 2,1 - BR 14 - diff --git a/arch/s390/lib/strcpy.S b/arch/s390/lib/strcpy.S deleted file mode 100644 index 1d36b9cb8..000000000 --- a/arch/s390/lib/strcpy.S +++ /dev/null @@ -1,20 +0,0 @@ -/* - * arch/s390/kernel/strcpy.S - * S390 strcpy routine - * - * S390 version - * Copyright (C) 2004 IBM Deutschland Entwicklung GmbH, IBM Corporation - * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), - */ - -/* - * R2 = address of destination - * R3 = address of source string - */ - .globl strcpy -strcpy: - sr %r0,%r0 -0: mvst %r2,%r3 - jo 0b - br %r14 - diff --git a/arch/s390/lib/strcpy64.S b/arch/s390/lib/strcpy64.S deleted file mode 100644 index 06815dcd7..000000000 --- a/arch/s390/lib/strcpy64.S +++ /dev/null @@ -1,20 +0,0 @@ -/* - * arch/s390/kernel/strcpy.S - * S390 strcpy routine - * - * S390 version - * Copyright (C) 2004 IBM Deutschland Entwicklung GmbH, IBM Corporation - * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), - */ - -/* - * R2 = address of destination - * R3 = address of source string - */ - .globl strcpy -strcpy: - sgr %r0,%r0 -0: mvst %r2,%r3 - jo 0b - br %r14 - diff --git a/arch/s390/lib/string.c b/arch/s390/lib/string.c new file mode 100644 index 000000000..dea4957d0 --- /dev/null +++ b/arch/s390/lib/string.c @@ -0,0 +1,405 @@ +/* + * arch/s390/lib/string.c + * Optimized string functions + * + * S390 version + * Copyright (C) 2004 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com) + */ + +#define IN_ARCH_STRING_C 1 + +#include +#include + +/* + * Helper functions to find the end of a string + */ +static inline char *__strend(const char *s) +{ + register unsigned long r0 asm("0") = 0; + + asm volatile ("0: srst %0,%1\n" + " jo 0b" + : "+d" (r0), "+a" (s) : : "cc" ); + return (char *) r0; +} + +static inline char *__strnend(const char *s, size_t n) +{ + register unsigned long r0 asm("0") = 0; + const char *p = s + n; + + asm volatile ("0: srst %0,%1\n" + " jo 0b" + : "+d" (p), "+a" (s) : "d" (r0) : "cc" ); + return (char *) p; +} + +/** + * strlen - Find the length of a string + * @s: The string to be sized + * + * returns the length of @s + */ +size_t strlen(const char *s) +{ + return __strend(s) - s; +} +EXPORT_SYMBOL_NOVERS(strlen); + +/** + * strnlen - Find the length of a length-limited string + * @s: The string to be sized + * @n: The maximum number of bytes to search + * + * returns the minimum of the length of @s and @n + */ +size_t strnlen(const char * s, size_t n) +{ + return __strnend(s, n) - s; +} +EXPORT_SYMBOL_NOVERS(strnlen); + +/** + * strcpy - Copy a %NUL terminated string + * @dest: Where to copy the string to + * @src: Where to copy the string from + * + * returns a pointer to @dest + */ +char *strcpy(char *dest, const char *src) +{ + register int r0 asm("0") = 0; + char *ret = dest; + + asm volatile ("0: mvst %0,%1\n" + " jo 0b" + : "+&a" (dest), "+&a" (src) : "d" (r0) + : "cc", "memory" ); + return ret; +} +EXPORT_SYMBOL_NOVERS(strcpy); + +/** + * strlcpy - Copy a %NUL terminated string into a sized buffer + * @dest: Where to copy the string to + * @src: Where to copy the string from + * @size: size of destination buffer + * + * Compatible with *BSD: the result is always a valid + * NUL-terminated string that fits in the buffer (unless, + * of course, the buffer size is zero). It does not pad + * out the result like strncpy() does. + */ +size_t strlcpy(char *dest, const char *src, size_t size) +{ + size_t ret = __strend(src) - src; + + if (size) { + size_t len = (ret >= size) ? size-1 : ret; + dest[len] = '\0'; + __builtin_memcpy(dest, src, len); + } + return ret; +} +EXPORT_SYMBOL_NOVERS(strlcpy); + +/** + * strncpy - Copy a length-limited, %NUL-terminated string + * @dest: Where to copy the string to + * @src: Where to copy the string from + * @n: The maximum number of bytes to copy + * + * The result is not %NUL-terminated if the source exceeds + * @n bytes. + */ +char *strncpy(char *dest, const char *src, size_t n) +{ + size_t len = __strnend(src, n) - src; + __builtin_memset(dest + len, 0, n - len); + __builtin_memcpy(dest, src, len); + return dest; +} +EXPORT_SYMBOL_NOVERS(strncpy); + +/** + * strcat - Append one %NUL-terminated string to another + * @dest: The string to be appended to + * @src: The string to append to it + * + * returns a pointer to @dest + */ +char *strcat(char *dest, const char *src) +{ + register int r0 asm("0") = 0; + unsigned long dummy; + char *ret = dest; + + asm volatile ("0: srst %0,%1\n" + " jo 0b\n" + "1: mvst %0,%2\n" + " jo 1b" + : "=&a" (dummy), "+a" (dest), "+a" (src) + : "d" (r0), "0" (0UL) : "cc", "memory" ); + return ret; +} +EXPORT_SYMBOL_NOVERS(strcat); + +/** + * strlcat - Append a length-limited, %NUL-terminated string to another + * @dest: The string to be appended to + * @src: The string to append to it + * @n: The size of the destination buffer. + */ +size_t strlcat(char *dest, const char *src, size_t n) +{ + size_t dsize = __strend(dest) - dest; + size_t len = __strend(src) - src; + size_t res = dsize + len; + + if (dsize < n) { + dest += dsize; + n -= dsize; + if (len >= n) + len = n - 1; + dest[len] = '\0'; + __builtin_memcpy(dest, src, len); + } + return res; +} +EXPORT_SYMBOL_NOVERS(strlcat); + +/** + * strncat - Append a length-limited, %NUL-terminated string to another + * @dest: The string to be appended to + * @src: The string to append to it + * @n: The maximum numbers of bytes to copy + * + * returns a pointer to @dest + * + * Note that in contrast to strncpy, strncat ensures the result is + * terminated. + */ +char *strncat(char *dest, const char *src, size_t n) +{ + size_t len = __strnend(src, n) - src; + char *p = __strend(dest); + + p[len] = '\0'; + __builtin_memcpy(p, src, len); + return dest; +} +EXPORT_SYMBOL_NOVERS(strncat); + +/** + * strcmp - Compare two strings + * @cs: One string + * @ct: Another string + * + * returns 0 if @cs and @ct are equal, + * < 0 if @cs is less than @ct + * > 0 if @cs is greater than @ct + */ +int strcmp(const char *cs, const char *ct) +{ + register int r0 asm("0") = 0; + int ret = 0; + + asm volatile ("0: clst %2,%3\n" + " jo 0b\n" + " je 1f\n" + " ic %0,0(%2)\n" + " ic %1,0(%3)\n" + " sr %0,%1\n" + "1:" + : "+d" (ret), "+d" (r0), "+a" (cs), "+a" (ct) + : : "cc" ); + return ret; +} +EXPORT_SYMBOL_NOVERS(strcmp); + +/** + * strrchr - Find the last occurrence of a character in a string + * @s: The string to be searched + * @c: The character to search for + */ +char * strrchr(const char * s, int c) +{ + size_t len = __strend(s) - s; + + if (len) + do { + if (s[len] == (char) c) + return (char *) s + len; + } while (--len > 0); + return 0; +} +EXPORT_SYMBOL_NOVERS(strrchr); + +/** + * strstr - Find the first substring in a %NUL terminated string + * @s1: The string to be searched + * @s2: The string to search for + */ +char * strstr(const char * s1,const char * s2) +{ + int l1, l2; + + l2 = __strend(s2) - s2; + if (!l2) + return (char *) s1; + l1 = __strend(s1) - s1; + while (l1-- >= l2) { + register unsigned long r2 asm("2") = (unsigned long) s1; + register unsigned long r3 asm("3") = (unsigned long) l2; + register unsigned long r4 asm("4") = (unsigned long) s2; + register unsigned long r5 asm("5") = (unsigned long) l2; + int cc; + + asm volatile ("0: clcle %1,%3,0\n" + " jo 0b\n" + " ipm %0\n" + " srl %0,28" + : "=&d" (cc), "+a" (r2), "+a" (r3), + "+a" (r4), "+a" (r5) : : "cc" ); + if (!cc) + return (char *) s1; + s1++; + } + return 0; +} +EXPORT_SYMBOL_NOVERS(strstr); + +/** + * memchr - Find a character in an area of memory. + * @s: The memory area + * @c: The byte to search for + * @n: The size of the area. + * + * returns the address of the first occurrence of @c, or %NULL + * if @c is not found + */ +void *memchr(const void *s, int c, size_t n) +{ + register int r0 asm("0") = (char) c; + const void *ret = s + n; + + asm volatile ("0: srst %0,%1\n" + " jo 0b\n" + " jl 1f\n" + " la %0,0\n" + "1:" + : "+a" (ret), "+&a" (s) : "d" (r0) : "cc" ); + return (void *) ret; +} +EXPORT_SYMBOL_NOVERS(memchr); + +/** + * memcmp - Compare two areas of memory + * @cs: One area of memory + * @ct: Another area of memory + * @count: The size of the area. + */ +int memcmp(const void *cs, const void *ct, size_t n) +{ + register unsigned long r2 asm("2") = (unsigned long) cs; + register unsigned long r3 asm("3") = (unsigned long) n; + register unsigned long r4 asm("4") = (unsigned long) ct; + register unsigned long r5 asm("5") = (unsigned long) n; + int ret; + + asm volatile ("0: clcle %1,%3,0\n" + " jo 0b\n" + " ipm %0\n" + " srl %0,28" + : "=&d" (ret), "+a" (r2), "+a" (r3), "+a" (r4), "+a" (r5) + : : "cc" ); + if (ret) + ret = *(char *) r2 - *(char *) r4; + return ret; +} +EXPORT_SYMBOL_NOVERS(memcmp); + +/** + * memscan - Find a character in an area of memory. + * @s: The memory area + * @c: The byte to search for + * @n: The size of the area. + * + * returns the address of the first occurrence of @c, or 1 byte past + * the area if @c is not found + */ +void *memscan(void *s, int c, size_t n) +{ + register int r0 asm("0") = (char) c; + const void *ret = s + n; + + asm volatile ("0: srst %0,%1\n" + " jo 0b\n" + : "+a" (ret), "+&a" (s) : "d" (r0) : "cc" ); + return (void *) ret; +} +EXPORT_SYMBOL_NOVERS(memscan); + +/** + * memcpy - Copy one area of memory to another + * @dest: Where to copy to + * @src: Where to copy from + * @n: The size of the area. + * + * returns a pointer to @dest + */ +void *memcpy(void *dest, const void *src, size_t n) +{ + return __builtin_memcpy(dest, src, n); +} +EXPORT_SYMBOL_NOVERS(memcpy); + +/** + * bcopy - Copy one area of memory to another + * @src: Where to copy from + * @dest: Where to copy to + * @n: The size of the area. + * + * Note that this is the same as memcpy(), with the arguments reversed. + * memcpy() is the standard, bcopy() is a legacy BSD function. + */ +void bcopy(const void *srcp, void *destp, size_t n) +{ + __builtin_memcpy(destp, srcp, n); +} +EXPORT_SYMBOL_NOVERS(bcopy); + +/** + * memset - Fill a region of memory with the given value + * @s: Pointer to the start of the area. + * @c: The byte to fill the area with + * @n: The size of the area. + * + * returns a pointer to @s + */ +void *memset(void *s, int c, size_t n) +{ + char *xs; + + if (c == 0) + return __builtin_memset(s, 0, n); + + xs = (char *) s; + if (n > 0) + do { + *xs++ = c; + } while (--n > 0); + return s; +} +EXPORT_SYMBOL_NOVERS(memset); + +/* + * missing exports for string functions defined in lib/string.c + */ +EXPORT_SYMBOL_NOVERS(memmove); +EXPORT_SYMBOL_NOVERS(strchr); +EXPORT_SYMBOL_NOVERS(strnchr); +EXPORT_SYMBOL_NOVERS(strncmp); +EXPORT_SYMBOL_NOVERS(strpbrk); diff --git a/arch/s390/lib/strncpy.S b/arch/s390/lib/strncpy.S deleted file mode 100644 index a3285bd04..000000000 --- a/arch/s390/lib/strncpy.S +++ /dev/null @@ -1,35 +0,0 @@ -/* - * arch/s390/kernel/strncpy.S - * S390 strncpy routine - * - * S390 version - * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation - * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), - */ - -/* - * R2 = address of destination - * R3 = address of source string - * R4 = max number of bytes to copy - */ - .globl strncpy -strncpy: - LR 1,2 # don't touch address in R2 - LTR 4,4 - JZ strncpy_exit # 0 bytes -> nothing to do - SR 0,0 -strncpy_loop: - ICM 0,1,0(3) # ICM sets the cc, IC does not - LA 3,1(3) - STC 0,0(1) - LA 1,1(1) - JZ strncpy_pad # ICM inserted a 0x00 - BRCT 4,strncpy_loop # R4 -= 1, jump to strncpy_loop if > 0 -strncpy_exit: - BR 14 -strncpy_clear: - STC 0,0(1) - LA 1,1(1) -strncpy_pad: - BRCT 4,strncpy_clear - BR 14 diff --git a/arch/s390/lib/strncpy64.S b/arch/s390/lib/strncpy64.S deleted file mode 100644 index 1e455e52b..000000000 --- a/arch/s390/lib/strncpy64.S +++ /dev/null @@ -1,35 +0,0 @@ -/* - * arch/s390/kernel/strncpy.S - * S390 strncpy routine - * - * S390 version - * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation - * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), - */ - -/* - * R2 = address of destination - * R3 = address of source string - * R4 = max number of bytes to copy - */ - .globl strncpy -strncpy: - LGR 1,2 # don't touch address in R2 - LTR 4,4 - JZ strncpy_exit # 0 bytes -> nothing to do - SGR 0,0 -strncpy_loop: - ICM 0,1,0(3) # ICM sets the cc, IC does not - LA 3,1(3) - STC 0,0(1) - LA 1,1(1) - JZ strncpy_pad # ICM inserted a 0x00 - BRCTG 4,strncpy_loop # R4 -= 1, jump to strncpy_loop if > 0 -strncpy_exit: - BR 14 -strncpy_clear: - STC 0,0(1) - LA 1,1(1) -strncpy_pad: - BRCTG 4,strncpy_clear - BR 14 diff --git a/arch/sparc64/lib/find_bit.c b/arch/sparc64/lib/find_bit.c new file mode 100644 index 000000000..420dfbafd --- /dev/null +++ b/arch/sparc64/lib/find_bit.c @@ -0,0 +1,125 @@ +#include + +/** + * find_next_bit - find the next set bit in a memory region + * @addr: The address to base the search on + * @offset: The bitnumber to start searching at + * @size: The maximum size to search + */ +unsigned long find_next_bit(unsigned long *addr, unsigned long size, unsigned long offset) +{ + unsigned long *p = addr + (offset >> 6); + unsigned long result = offset & ~63UL; + unsigned long tmp; + + if (offset >= size) + return size; + size -= result; + offset &= 63UL; + if (offset) { + tmp = *(p++); + tmp &= (~0UL << offset); + if (size < 64) + goto found_first; + if (tmp) + goto found_middle; + size -= 64; + result += 64; + } + while (size & ~63UL) { + if ((tmp = *(p++))) + goto found_middle; + result += 64; + size -= 64; + } + if (!size) + return result; + tmp = *p; + +found_first: + tmp &= (~0UL >> (64 - size)); + if (tmp == 0UL) /* Are any bits set? */ + return result + size; /* Nope. */ +found_middle: + return result + __ffs(tmp); +} + +/* find_next_zero_bit() finds the first zero bit in a bit string of length + * 'size' bits, starting the search at bit 'offset'. This is largely based + * on Linus's ALPHA routines, which are pretty portable BTW. + */ + +unsigned long find_next_zero_bit(unsigned long *addr, unsigned long size, unsigned long offset) +{ + unsigned long *p = addr + (offset >> 6); + unsigned long result = offset & ~63UL; + unsigned long tmp; + + if (offset >= size) + return size; + size -= result; + offset &= 63UL; + if (offset) { + tmp = *(p++); + tmp |= ~0UL >> (64-offset); + if (size < 64) + goto found_first; + if (~tmp) + goto found_middle; + size -= 64; + result += 64; + } + while (size & ~63UL) { + if (~(tmp = *(p++))) + goto found_middle; + result += 64; + size -= 64; + } + if (!size) + return result; + tmp = *p; + +found_first: + tmp |= ~0UL << size; + if (tmp == ~0UL) /* Are any bits zero? */ + return result + size; /* Nope. */ +found_middle: + return result + ffz(tmp); +} + +unsigned long find_next_zero_le_bit(unsigned long *addr, unsigned long size, unsigned long offset) +{ + unsigned long *p = addr + (offset >> 6); + unsigned long result = offset & ~63UL; + unsigned long tmp; + + if (offset >= size) + return size; + size -= result; + offset &= 63UL; + if(offset) { + tmp = __swab64p(p++); + tmp |= (~0UL >> (64-offset)); + if(size < 64) + goto found_first; + if(~tmp) + goto found_middle; + size -= 64; + result += 64; + } + while(size & ~63) { + if(~(tmp = __swab64p(p++))) + goto found_middle; + result += 64; + size -= 64; + } + if(!size) + return result; + tmp = __swab64p(p); +found_first: + tmp |= (~0UL << size); + if (tmp == ~0UL) /* Are any bits zero? */ + return result + size; /* Nope. */ +found_middle: + return result + ffz(tmp); +} diff --git a/arch/sparc64/lib/splock.S b/arch/sparc64/lib/splock.S new file mode 100644 index 000000000..d17a3badd --- /dev/null +++ b/arch/sparc64/lib/splock.S @@ -0,0 +1,23 @@ +/* splock.S: Spinlock primitives too large to inline. + * + * Copyright (C) 2004 David S. Miller (davem@redhat.com) + */ + + .text + .align 64 + + .globl _raw_spin_lock_flags +_raw_spin_lock_flags: /* %o0 = lock_ptr, %o1 = irq_flags */ +1: ldstub [%o0], %g7 + brnz,pn %g7, 2f + membar #StoreLoad | #StoreStore + retl + nop + +2: rdpr %pil, %g2 ! Save PIL + wrpr %o1, %pil ! Set previous PIL +3: ldub [%o0], %g7 ! Spin on lock set + brnz,pt %g7, 3b + membar #LoadLoad + ba,pt %xcc, 1b ! Retry lock acquire + wrpr %g2, %pil ! Restore PIL diff --git a/arch/um/drivers/cow.h b/arch/um/drivers/cow.h deleted file mode 100644 index d875d0435..000000000 --- a/arch/um/drivers/cow.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef __COW_H__ -#define __COW_H__ - -#include - -#if __BYTE_ORDER == __BIG_ENDIAN -# define ntohll(x) (x) -# define htonll(x) (x) -#elif __BYTE_ORDER == __LITTLE_ENDIAN -# define ntohll(x) bswap_64(x) -# define htonll(x) bswap_64(x) -#else -#error "__BYTE_ORDER not defined" -#endif - -extern int init_cow_file(int fd, char *cow_file, char *backing_file, - int sectorsize, int alignment, int *bitmap_offset_out, - unsigned long *bitmap_len_out, int *data_offset_out); - -extern int file_reader(__u64 offset, char *buf, int len, void *arg); -extern int read_cow_header(int (*reader)(__u64, char *, int, void *), - void *arg, __u32 *version_out, - char **backing_file_out, time_t *mtime_out, - __u64 *size_out, int *sectorsize_out, - __u32 *align_out, int *bitmap_offset_out); - -extern int write_cow_header(char *cow_file, int fd, char *backing_file, - int sectorsize, int alignment, long long *size); - -extern void cow_sizes(int version, __u64 size, int sectorsize, int align, - int bitmap_offset, unsigned long *bitmap_len_out, - int *data_offset_out); - -#endif - -/* - * --------------------------------------------------------------------------- - * Local variables: - * c-file-style: "linux" - * End: - */ diff --git a/arch/um/drivers/cow_user.c b/arch/um/drivers/cow_user.c deleted file mode 100644 index 014c2c853..000000000 --- a/arch/um/drivers/cow_user.c +++ /dev/null @@ -1,375 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "os.h" - -#include "cow.h" -#include "cow_sys.h" - -#define PATH_LEN_V1 256 - -struct cow_header_v1 { - int magic; - int version; - char backing_file[PATH_LEN_V1]; - time_t mtime; - __u64 size; - int sectorsize; -}; - -#define PATH_LEN_V2 MAXPATHLEN - -struct cow_header_v2 { - unsigned long magic; - unsigned long version; - char backing_file[PATH_LEN_V2]; - time_t mtime; - __u64 size; - int sectorsize; -}; - -/* Define PATH_LEN_V3 as the usual value of MAXPATHLEN, just hard-code it in - * case other systems have different values for MAXPATHLEN - */ -#define PATH_LEN_V3 4096 - -/* Changes from V2 - - * PATH_LEN_V3 as described above - * Explicitly specify field bit lengths for systems with different - * lengths for the usual C types. Not sure whether char or - * time_t should be changed, this can be changed later without - * breaking compatibility - * Add alignment field so that different alignments can be used for the - * bitmap and data - * Add cow_format field to allow for the possibility of different ways - * of specifying the COW blocks. For now, the only value is 0, - * for the traditional COW bitmap. - * Move the backing_file field to the end of the header. This allows - * for the possibility of expanding it into the padding required - * by the bitmap alignment. - * The bitmap and data portions of the file will be aligned as specified - * by the alignment field. This is to allow COW files to be - * put on devices with restrictions on access alignments, such as - * /dev/raw, with a 512 byte alignment restriction. This also - * allows the data to be more aligned more strictly than on - * sector boundaries. This is needed for ubd-mmap, which needs - * the data to be page aligned. - * Fixed (finally!) the rounding bug - */ - -struct cow_header_v3 { - __u32 magic; - __u32 version; - time_t mtime; - __u64 size; - __u32 sectorsize; - __u32 alignment; - __u32 cow_format; - char backing_file[PATH_LEN_V3]; -}; - -/* COW format definitions - for now, we have only the usual COW bitmap */ -#define COW_BITMAP 0 - -union cow_header { - struct cow_header_v1 v1; - struct cow_header_v2 v2; - struct cow_header_v3 v3; -}; - -#define COW_MAGIC 0x4f4f4f4d /* MOOO */ -#define COW_VERSION 3 - -#define DIV_ROUND(x, len) (((x) + (len) - 1) / (len)) -#define ROUND_UP(x, align) DIV_ROUND(x, align) * (align) - -void cow_sizes(int version, __u64 size, int sectorsize, int align, - int bitmap_offset, unsigned long *bitmap_len_out, - int *data_offset_out) -{ - if(version < 3){ - *bitmap_len_out = (size + sectorsize - 1) / (8 * sectorsize); - - *data_offset_out = bitmap_offset + *bitmap_len_out; - *data_offset_out = (*data_offset_out + sectorsize - 1) / - sectorsize; - *data_offset_out *= sectorsize; - } - else { - *bitmap_len_out = DIV_ROUND(size, sectorsize); - *bitmap_len_out = DIV_ROUND(*bitmap_len_out, 8); - - *data_offset_out = bitmap_offset + *bitmap_len_out; - *data_offset_out = ROUND_UP(*data_offset_out, align); - } -} - -static int absolutize(char *to, int size, char *from) -{ - char save_cwd[256], *slash; - int remaining; - - if(getcwd(save_cwd, sizeof(save_cwd)) == NULL) { - cow_printf("absolutize : unable to get cwd - errno = %d\n", - errno); - return(-1); - } - slash = strrchr(from, '/'); - if(slash != NULL){ - *slash = '\0'; - if(chdir(from)){ - *slash = '/'; - cow_printf("absolutize : Can't cd to '%s' - " - "errno = %d\n", from, errno); - return(-1); - } - *slash = '/'; - if(getcwd(to, size) == NULL){ - cow_printf("absolutize : unable to get cwd of '%s' - " - "errno = %d\n", from, errno); - return(-1); - } - remaining = size - strlen(to); - if(strlen(slash) + 1 > remaining){ - cow_printf("absolutize : unable to fit '%s' into %d " - "chars\n", from, size); - return(-1); - } - strcat(to, slash); - } - else { - if(strlen(save_cwd) + 1 + strlen(from) + 1 > size){ - cow_printf("absolutize : unable to fit '%s' into %d " - "chars\n", from, size); - return(-1); - } - strcpy(to, save_cwd); - strcat(to, "/"); - strcat(to, from); - } - chdir(save_cwd); - return(0); -} - -int write_cow_header(char *cow_file, int fd, char *backing_file, - int sectorsize, int alignment, long long *size) -{ - struct cow_header_v3 *header; - unsigned long modtime; - int err; - - err = cow_seek_file(fd, 0); - if(err < 0){ - cow_printf("write_cow_header - lseek failed, err = %d\n", -err); - goto out; - } - - err = -ENOMEM; - header = cow_malloc(sizeof(*header)); - if(header == NULL){ - cow_printf("Failed to allocate COW V3 header\n"); - goto out; - } - header->magic = htonl(COW_MAGIC); - header->version = htonl(COW_VERSION); - - err = -EINVAL; - if(strlen(backing_file) > sizeof(header->backing_file) - 1){ - cow_printf("Backing file name \"%s\" is too long - names are " - "limited to %d characters\n", backing_file, - sizeof(header->backing_file) - 1); - goto out_free; - } - - if(absolutize(header->backing_file, sizeof(header->backing_file), - backing_file)) - goto out_free; - - err = os_file_modtime(header->backing_file, &modtime); - if(err < 0){ - cow_printf("Backing file '%s' mtime request failed, " - "err = %d\n", header->backing_file, -err); - goto out_free; - } - - err = cow_file_size(header->backing_file, size); - if(err < 0){ - cow_printf("Couldn't get size of backing file '%s', " - "err = %d\n", header->backing_file, -err); - goto out_free; - } - - header->mtime = htonl(modtime); - header->size = htonll(*size); - header->sectorsize = htonl(sectorsize); - header->alignment = htonl(alignment); - header->cow_format = COW_BITMAP; - - err = os_write_file(fd, header, sizeof(*header)); - if(err != sizeof(*header)){ - cow_printf("Write of header to new COW file '%s' failed, " - "err = %d\n", cow_file, -err); - goto out_free; - } - err = 0; - out_free: - cow_free(header); - out: - return(err); -} - -int file_reader(__u64 offset, char *buf, int len, void *arg) -{ - int fd = *((int *) arg); - - return(pread(fd, buf, len, offset)); -} - -/* XXX Need to sanity-check the values read from the header */ - -int read_cow_header(int (*reader)(__u64, char *, int, void *), void *arg, - __u32 *version_out, char **backing_file_out, - time_t *mtime_out, __u64 *size_out, - int *sectorsize_out, __u32 *align_out, - int *bitmap_offset_out) -{ - union cow_header *header; - char *file; - int err, n; - unsigned long version, magic; - - header = cow_malloc(sizeof(*header)); - if(header == NULL){ - cow_printf("read_cow_header - Failed to allocate header\n"); - return(-ENOMEM); - } - err = -EINVAL; - n = (*reader)(0, (char *) header, sizeof(*header), arg); - if(n < offsetof(typeof(header->v1), backing_file)){ - cow_printf("read_cow_header - short header\n"); - goto out; - } - - magic = header->v1.magic; - if(magic == COW_MAGIC) { - version = header->v1.version; - } - else if(magic == ntohl(COW_MAGIC)){ - version = ntohl(header->v1.version); - } - /* No error printed because the non-COW case comes through here */ - else goto out; - - *version_out = version; - - if(version == 1){ - if(n < sizeof(header->v1)){ - cow_printf("read_cow_header - failed to read V1 " - "header\n"); - goto out; - } - *mtime_out = header->v1.mtime; - *size_out = header->v1.size; - *sectorsize_out = header->v1.sectorsize; - *bitmap_offset_out = sizeof(header->v1); - *align_out = *sectorsize_out; - file = header->v1.backing_file; - } - else if(version == 2){ - if(n < sizeof(header->v2)){ - cow_printf("read_cow_header - failed to read V2 " - "header\n"); - goto out; - } - *mtime_out = ntohl(header->v2.mtime); - *size_out = ntohll(header->v2.size); - *sectorsize_out = ntohl(header->v2.sectorsize); - *bitmap_offset_out = sizeof(header->v2); - *align_out = *sectorsize_out; - file = header->v2.backing_file; - } - else if(version == 3){ - if(n < sizeof(header->v3)){ - cow_printf("read_cow_header - failed to read V2 " - "header\n"); - goto out; - } - *mtime_out = ntohl(header->v3.mtime); - *size_out = ntohll(header->v3.size); - *sectorsize_out = ntohl(header->v3.sectorsize); - *align_out = ntohl(header->v3.alignment); - *bitmap_offset_out = ROUND_UP(sizeof(header->v3), *align_out); - file = header->v3.backing_file; - } - else { - cow_printf("read_cow_header - invalid COW version\n"); - goto out; - } - err = -ENOMEM; - *backing_file_out = cow_strdup(file); - if(*backing_file_out == NULL){ - cow_printf("read_cow_header - failed to allocate backing " - "file\n"); - goto out; - } - err = 0; - out: - cow_free(header); - return(err); -} - -int init_cow_file(int fd, char *cow_file, char *backing_file, int sectorsize, - int alignment, int *bitmap_offset_out, - unsigned long *bitmap_len_out, int *data_offset_out) -{ - __u64 size, offset; - char zero = 0; - int err; - - err = write_cow_header(cow_file, fd, backing_file, sectorsize, - alignment, &size); - if(err) - goto out; - - *bitmap_offset_out = ROUND_UP(sizeof(struct cow_header_v3), alignment); - cow_sizes(COW_VERSION, size, sectorsize, alignment, *bitmap_offset_out, - bitmap_len_out, data_offset_out); - - offset = *data_offset_out + size - sizeof(zero); - err = cow_seek_file(fd, offset); - if(err < 0){ - cow_printf("cow bitmap lseek failed : err = %d\n", -err); - goto out; - } - - /* does not really matter how much we write it is just to set EOF - * this also sets the entire COW bitmap - * to zero without having to allocate it - */ - err = cow_write_file(fd, &zero, sizeof(zero)); - if(err != sizeof(zero)){ - cow_printf("Write of bitmap to new COW file '%s' failed, " - "err = %d\n", cow_file, -err); - err = -EINVAL; - goto out; - } - - return(0); - - out: - return(err); -} - -/* - * --------------------------------------------------------------------------- - * Local variables: - * c-file-style: "linux" - * End: - */ diff --git a/arch/um/include/irq_kern.h b/arch/um/include/irq_kern.h deleted file mode 100644 index 4bcb829d7..000000000 --- a/arch/um/include/irq_kern.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com) - * Licensed under the GPL - */ - -#ifndef __IRQ_KERN_H__ -#define __IRQ_KERN_H__ - -#include "linux/interrupt.h" - -extern int um_request_irq(unsigned int irq, int fd, int type, - irqreturn_t (*handler)(int, void *, - struct pt_regs *), - unsigned long irqflags, const char * devname, - void *dev_id); - -#endif - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * Emacs will notice this stuff at the end of the file and automatically - * adjust the settings for this buffer only. This must remain at the end - * of the file. - * --------------------------------------------------------------------------- - * Local variables: - * c-file-style: "linux" - * End: - */ diff --git a/arch/um/include/mem_kern.h b/arch/um/include/mem_kern.h deleted file mode 100644 index b39f03d94..000000000 --- a/arch/um/include/mem_kern.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2003 Jeff Dike (jdike@addtoit.com) - * Licensed under the GPL - */ - -#ifndef __MEM_KERN_H__ -#define __MEM_KERN_H__ - -#include "linux/list.h" -#include "linux/types.h" - -struct remapper { - struct list_head list; - int (*proc)(int, unsigned long, int, __u64); -}; - -extern void register_remapper(struct remapper *info); - -#endif - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * Emacs will notice this stuff at the end of the file and automatically - * adjust the settings for this buffer only. This must remain at the end - * of the file. - * --------------------------------------------------------------------------- - * Local variables: - * c-file-style: "linux" - * End: - */ diff --git a/arch/um/kernel/physmem.c b/arch/um/kernel/physmem.c deleted file mode 100644 index d0e0f50dc..000000000 --- a/arch/um/kernel/physmem.c +++ /dev/null @@ -1,468 +0,0 @@ -/* - * Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com) - * Licensed under the GPL - */ - -#include "linux/mm.h" -#include "linux/ghash.h" -#include "linux/slab.h" -#include "linux/vmalloc.h" -#include "linux/bootmem.h" -#include "asm/types.h" -#include "asm/pgtable.h" -#include "kern_util.h" -#include "user_util.h" -#include "mode_kern.h" -#include "mem.h" -#include "mem_user.h" -#include "os.h" -#include "kern.h" -#include "init.h" - -#if 0 -static pgd_t physmem_pgd[PTRS_PER_PGD]; - -static struct phys_desc *lookup_mapping(void *addr) -{ - pgd = &physmem_pgd[pgd_index(addr)]; - if(pgd_none(pgd)) - return(NULL); - - pmd = pmd_offset(pgd, addr); - if(pmd_none(pmd)) - return(NULL); - - pte = pte_offset_kernel(pmd, addr); - return((struct phys_desc *) pte_val(pte)); -} - -static struct add_mapping(void *addr, struct phys_desc *new) -{ -} -#endif - -#define PHYS_HASHSIZE (8192) - -struct phys_desc; - -DEF_HASH_STRUCTS(virtmem, PHYS_HASHSIZE, struct phys_desc); - -struct phys_desc { - struct virtmem_ptrs virt_ptrs; - int fd; - __u64 offset; - void *virt; - unsigned long phys; - struct list_head list; -}; - -struct virtmem_table virtmem_hash; - -static int virt_cmp(void *virt1, void *virt2) -{ - return(virt1 != virt2); -} - -static int virt_hash(void *virt) -{ - unsigned long addr = ((unsigned long) virt) >> PAGE_SHIFT; - return(addr % PHYS_HASHSIZE); -} - -DEF_HASH(static, virtmem, struct phys_desc, virt_ptrs, void *, virt, virt_cmp, - virt_hash); - -LIST_HEAD(descriptor_mappings); - -struct desc_mapping { - int fd; - struct list_head list; - struct list_head pages; -}; - -static struct desc_mapping *find_mapping(int fd) -{ - struct desc_mapping *desc; - struct list_head *ele; - - list_for_each(ele, &descriptor_mappings){ - desc = list_entry(ele, struct desc_mapping, list); - if(desc->fd == fd) - return(desc); - } - - return(NULL); -} - -static struct desc_mapping *descriptor_mapping(int fd) -{ - struct desc_mapping *desc; - - desc = find_mapping(fd); - if(desc != NULL) - return(desc); - - desc = kmalloc(sizeof(*desc), GFP_ATOMIC); - if(desc == NULL) - return(NULL); - - *desc = ((struct desc_mapping) - { .fd = fd, - .list = LIST_HEAD_INIT(desc->list), - .pages = LIST_HEAD_INIT(desc->pages) }); - list_add(&desc->list, &descriptor_mappings); - - return(desc); -} - -int physmem_subst_mapping(void *virt, int fd, __u64 offset, int w) -{ - struct desc_mapping *fd_maps; - struct phys_desc *desc; - unsigned long phys; - int err; - - fd_maps = descriptor_mapping(fd); - if(fd_maps == NULL) - return(-ENOMEM); - - phys = __pa(virt); - if(find_virtmem_hash(&virtmem_hash, virt) != NULL) - panic("Address 0x%p is already substituted\n", virt); - - err = -ENOMEM; - desc = kmalloc(sizeof(*desc), GFP_ATOMIC); - if(desc == NULL) - goto out; - - *desc = ((struct phys_desc) - { .virt_ptrs = { NULL, NULL }, - .fd = fd, - .offset = offset, - .virt = virt, - .phys = __pa(virt), - .list = LIST_HEAD_INIT(desc->list) }); - insert_virtmem_hash(&virtmem_hash, desc); - - list_add(&desc->list, &fd_maps->pages); - - virt = (void *) ((unsigned long) virt & PAGE_MASK); - err = os_map_memory(virt, fd, offset, PAGE_SIZE, 1, w, 0); - if(!err) - goto out; - - remove_virtmem_hash(&virtmem_hash, desc); - kfree(desc); - out: - return(err); -} - -static int physmem_fd = -1; - -static void remove_mapping(struct phys_desc *desc) -{ - void *virt = desc->virt; - int err; - - remove_virtmem_hash(&virtmem_hash, desc); - list_del(&desc->list); - kfree(desc); - - err = os_map_memory(virt, physmem_fd, __pa(virt), PAGE_SIZE, 1, 1, 0); - if(err) - panic("Failed to unmap block device page from physical memory, " - "errno = %d", -err); -} - -int physmem_remove_mapping(void *virt) -{ - struct phys_desc *desc; - - virt = (void *) ((unsigned long) virt & PAGE_MASK); - desc = find_virtmem_hash(&virtmem_hash, virt); - if(desc == NULL) - return(0); - - remove_mapping(desc); - return(1); -} - -void physmem_forget_descriptor(int fd) -{ - struct desc_mapping *desc; - struct phys_desc *page; - struct list_head *ele, *next; - __u64 offset; - void *addr; - int err; - - desc = find_mapping(fd); - if(desc == NULL) - return; - - list_for_each_safe(ele, next, &desc->pages){ - page = list_entry(ele, struct phys_desc, list); - offset = page->offset; - addr = page->virt; - remove_mapping(page); - err = os_seek_file(fd, offset); - if(err) - panic("physmem_forget_descriptor - failed to seek " - "to %lld in fd %d, error = %d\n", - offset, fd, -err); - err = os_read_file(fd, addr, PAGE_SIZE); - if(err < 0) - panic("physmem_forget_descriptor - failed to read " - "from fd %d to 0x%p, error = %d\n", - fd, addr, -err); - } - - list_del(&desc->list); - kfree(desc); -} - -void arch_free_page(struct page *page, int order) -{ - void *virt; - int i; - - for(i = 0; i < (1 << order); i++){ - virt = __va(page_to_phys(page + i)); - physmem_remove_mapping(virt); - } -} - -int is_remapped(void *virt) -{ - return(find_virtmem_hash(&virtmem_hash, virt) != NULL); -} - -/* Changed during early boot */ -unsigned long high_physmem; - -extern unsigned long physmem_size; - -void *to_virt(unsigned long phys) -{ - return((void *) uml_physmem + phys); -} - -unsigned long to_phys(void *virt) -{ - return(((unsigned long) virt) - uml_physmem); -} - -int init_maps(unsigned long physmem, unsigned long iomem, unsigned long highmem) -{ - struct page *p, *map; - unsigned long phys_len, phys_pages, highmem_len, highmem_pages; - unsigned long iomem_len, iomem_pages, total_len, total_pages; - int i; - - phys_pages = physmem >> PAGE_SHIFT; - phys_len = phys_pages * sizeof(struct page); - - iomem_pages = iomem >> PAGE_SHIFT; - iomem_len = iomem_pages * sizeof(struct page); - - highmem_pages = highmem >> PAGE_SHIFT; - highmem_len = highmem_pages * sizeof(struct page); - - total_pages = phys_pages + iomem_pages + highmem_pages; - total_len = phys_len + iomem_pages + highmem_len; - - if(kmalloc_ok){ - map = kmalloc(total_len, GFP_KERNEL); - if(map == NULL) - map = vmalloc(total_len); - } - else map = alloc_bootmem_low_pages(total_len); - - if(map == NULL) - return(-ENOMEM); - - for(i = 0; i < total_pages; i++){ - p = &map[i]; - set_page_count(p, 0); - SetPageReserved(p); - INIT_LIST_HEAD(&p->lru); - } - - mem_map = map; - max_mapnr = total_pages; - return(0); -} - -struct page *phys_to_page(const unsigned long phys) -{ - return(&mem_map[phys >> PAGE_SHIFT]); -} - -struct page *__virt_to_page(const unsigned long virt) -{ - return(&mem_map[__pa(virt) >> PAGE_SHIFT]); -} - -unsigned long page_to_phys(struct page *page) -{ - return((page - mem_map) << PAGE_SHIFT); -} - -pte_t mk_pte(struct page *page, pgprot_t pgprot) -{ - pte_t pte; - - pte_val(pte) = page_to_phys(page) + pgprot_val(pgprot); - if(pte_present(pte)) pte_mknewprot(pte_mknewpage(pte)); - return(pte); -} - -/* Changed during early boot */ -static unsigned long kmem_top = 0; - -unsigned long get_kmem_end(void) -{ - if(kmem_top == 0) - kmem_top = CHOOSE_MODE(kmem_end_tt, kmem_end_skas); - return(kmem_top); -} - -void map_memory(unsigned long virt, unsigned long phys, unsigned long len, - int r, int w, int x) -{ - __u64 offset; - int fd, err; - - fd = phys_mapping(phys, &offset); - err = os_map_memory((void *) virt, fd, offset, len, r, w, x); - if(err) - panic("map_memory(0x%lx, %d, 0x%llx, %ld, %d, %d, %d) failed, " - "err = %d\n", virt, fd, offset, len, r, w, x, err); -} - -#define PFN_UP(x) (((x) + PAGE_SIZE-1) >> PAGE_SHIFT) - -void setup_physmem(unsigned long start, unsigned long reserve_end, - unsigned long len, unsigned long highmem) -{ - unsigned long reserve = reserve_end - start; - int pfn = PFN_UP(__pa(reserve_end)); - int delta = (len - reserve) >> PAGE_SHIFT; - int err, offset, bootmap_size; - - physmem_fd = create_mem_file(len + highmem); - - offset = uml_reserved - uml_physmem; - err = os_map_memory((void *) uml_reserved, physmem_fd, offset, - len - offset, 1, 1, 0); - if(err < 0){ - os_print_error(err, "Mapping memory"); - exit(1); - } - - bootmap_size = init_bootmem(pfn, pfn + delta); - free_bootmem(__pa(reserve_end) + bootmap_size, - len - bootmap_size - reserve); -} - -int phys_mapping(unsigned long phys, __u64 *offset_out) -{ - struct phys_desc *desc = find_virtmem_hash(&virtmem_hash, - __va(phys & PAGE_MASK)); - int fd = -1; - - if(desc != NULL){ - fd = desc->fd; - *offset_out = desc->offset; - } - else if(phys < physmem_size){ - fd = physmem_fd; - *offset_out = phys; - } - else if(phys < __pa(end_iomem)){ - struct iomem_region *region = iomem_regions; - - while(region != NULL){ - if((phys >= region->phys) && - (phys < region->phys + region->size)){ - fd = region->fd; - *offset_out = phys - region->phys; - break; - } - region = region->next; - } - } - else if(phys < __pa(end_iomem) + highmem){ - fd = physmem_fd; - *offset_out = phys - iomem_size; - } - - return(fd); -} - -static int __init uml_mem_setup(char *line, int *add) -{ - char *retptr; - physmem_size = memparse(line,&retptr); - return 0; -} -__uml_setup("mem=", uml_mem_setup, -"mem=\n" -" This controls how much \"physical\" memory the kernel allocates\n" -" for the system. The size is specified as a number followed by\n" -" one of 'k', 'K', 'm', 'M', which have the obvious meanings.\n" -" This is not related to the amount of memory in the host. It can\n" -" be more, and the excess, if it's ever used, will just be swapped out.\n" -" Example: mem=64M\n\n" -); - -unsigned long find_iomem(char *driver, unsigned long *len_out) -{ - struct iomem_region *region = iomem_regions; - - while(region != NULL){ - if(!strcmp(region->driver, driver)){ - *len_out = region->size; - return(region->virt); - } - } - - return(0); -} - -int setup_iomem(void) -{ - struct iomem_region *region = iomem_regions; - unsigned long iomem_start = high_physmem + PAGE_SIZE; - int err; - - while(region != NULL){ - err = os_map_memory((void *) iomem_start, region->fd, 0, - region->size, 1, 1, 0); - if(err) - printk("Mapping iomem region for driver '%s' failed, " - "errno = %d\n", region->driver, -err); - else { - region->virt = iomem_start; - region->phys = __pa(region->virt); - } - - iomem_start += region->size + PAGE_SIZE; - region = region->next; - } - - return(0); -} - -__initcall(setup_iomem); - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * Emacs will notice this stuff at the end of the file and automatically - * adjust the settings for this buffer only. This must remain at the end - * of the file. - * --------------------------------------------------------------------------- - * Local variables: - * c-file-style: "linux" - * End: - */ diff --git a/arch/um/kernel/skas/uaccess.c b/arch/um/kernel/skas/uaccess.c deleted file mode 100644 index ea82f19b2..000000000 --- a/arch/um/kernel/skas/uaccess.c +++ /dev/null @@ -1,219 +0,0 @@ -/* - * Copyright (C) 2002 - 2003 Jeff Dike (jdike@addtoit.com) - * Licensed under the GPL - */ - -#include "linux/stddef.h" -#include "linux/kernel.h" -#include "linux/string.h" -#include "linux/fs.h" -#include "linux/highmem.h" -#include "asm/page.h" -#include "asm/pgtable.h" -#include "asm/uaccess.h" -#include "kern_util.h" - -extern void *um_virt_to_phys(struct task_struct *task, unsigned long addr, - pte_t *pte_out); - -static unsigned long maybe_map(unsigned long virt, int is_write) -{ - pte_t pte; - int err; - - void *phys = um_virt_to_phys(current, virt, &pte); - int dummy_code; - - if(IS_ERR(phys) || (is_write && !pte_write(pte))){ - err = handle_page_fault(virt, 0, is_write, 0, &dummy_code); - if(err) - return(0); - phys = um_virt_to_phys(current, virt, NULL); - } - return((unsigned long) phys); -} - -static int do_op(unsigned long addr, int len, int is_write, - int (*op)(unsigned long addr, int len, void *arg), void *arg) -{ - struct page *page; - int n; - - addr = maybe_map(addr, is_write); - if(addr == -1) - return(-1); - - page = phys_to_page(addr); - addr = (unsigned long) kmap(page) + (addr & ~PAGE_MASK); - n = (*op)(addr, len, arg); - kunmap(page); - - return(n); -} - -static int buffer_op(unsigned long addr, int len, int is_write, - int (*op)(unsigned long addr, int len, void *arg), - void *arg) -{ - int size = min(PAGE_ALIGN(addr) - addr, (unsigned long) len); - int remain = len, n; - - n = do_op(addr, size, is_write, op, arg); - if(n != 0) - return(n < 0 ? remain : 0); - - addr += size; - remain -= size; - if(remain == 0) - return(0); - - while(addr < ((addr + remain) & PAGE_MASK)){ - n = do_op(addr, PAGE_SIZE, is_write, op, arg); - if(n != 0) - return(n < 0 ? remain : 0); - - addr += PAGE_SIZE; - remain -= PAGE_SIZE; - } - if(remain == 0) - return(0); - - n = do_op(addr, remain, is_write, op, arg); - if(n != 0) - return(n < 0 ? remain : 0); - return(0); -} - -static int copy_chunk_from_user(unsigned long from, int len, void *arg) -{ - unsigned long *to_ptr = arg, to = *to_ptr; - - memcpy((void *) to, (void *) from, len); - *to_ptr += len; - return(0); -} - -int copy_from_user_skas(void *to, const void *from, int n) -{ - if(segment_eq(get_fs(), KERNEL_DS)){ - memcpy(to, from, n); - return(0); - } - - return(access_ok_skas(VERIFY_READ, from, n) ? - buffer_op((unsigned long) from, n, 0, copy_chunk_from_user, &to): - n); -} - -static int copy_chunk_to_user(unsigned long to, int len, void *arg) -{ - unsigned long *from_ptr = arg, from = *from_ptr; - - memcpy((void *) to, (void *) from, len); - *from_ptr += len; - return(0); -} - -int copy_to_user_skas(void *to, const void *from, int n) -{ - if(segment_eq(get_fs(), KERNEL_DS)){ - memcpy(to, from, n); - return(0); - } - - return(access_ok_skas(VERIFY_WRITE, to, n) ? - buffer_op((unsigned long) to, n, 1, copy_chunk_to_user, &from) : - n); -} - -static int strncpy_chunk_from_user(unsigned long from, int len, void *arg) -{ - char **to_ptr = arg, *to = *to_ptr; - int n; - - strncpy(to, (void *) from, len); - n = strnlen(to, len); - *to_ptr += n; - - if(n < len) - return(1); - return(0); -} - -int strncpy_from_user_skas(char *dst, const char *src, int count) -{ - int n; - char *ptr = dst; - - if(segment_eq(get_fs(), KERNEL_DS)){ - strncpy(dst, src, count); - return(strnlen(dst, count)); - } - - if(!access_ok_skas(VERIFY_READ, src, 1)) - return(-EFAULT); - - n = buffer_op((unsigned long) src, count, 0, strncpy_chunk_from_user, - &ptr); - if(n != 0) - return(-EFAULT); - return(strnlen(dst, count)); -} - -static int clear_chunk(unsigned long addr, int len, void *unused) -{ - memset((void *) addr, 0, len); - return(0); -} - -int __clear_user_skas(void *mem, int len) -{ - return(buffer_op((unsigned long) mem, len, 1, clear_chunk, NULL)); -} - -int clear_user_skas(void *mem, int len) -{ - if(segment_eq(get_fs(), KERNEL_DS)){ - memset(mem, 0, len); - return(0); - } - - return(access_ok_skas(VERIFY_WRITE, mem, len) ? - buffer_op((unsigned long) mem, len, 1, clear_chunk, NULL) : len); -} - -static int strnlen_chunk(unsigned long str, int len, void *arg) -{ - int *len_ptr = arg, n; - - n = strnlen((void *) str, len); - *len_ptr += n; - - if(n < len) - return(1); - return(0); -} - -int strnlen_user_skas(const void *str, int len) -{ - int count = 0, n; - - if(segment_eq(get_fs(), KERNEL_DS)) - return(strnlen(str, len) + 1); - - n = buffer_op((unsigned long) str, len, 0, strnlen_chunk, &count); - if(n == 0) - return(count + 1); - return(-EFAULT); -} - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * Emacs will notice this stuff at the end of the file and automatically - * adjust the settings for this buffer only. This must remain at the end - * of the file. - * --------------------------------------------------------------------------- - * Local variables: - * c-file-style: "linux" - * End: - */ diff --git a/arch/um/kernel/tt/uaccess.c b/arch/um/kernel/tt/uaccess.c deleted file mode 100644 index 9c8401120..000000000 --- a/arch/um/kernel/tt/uaccess.c +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com) - * Licensed under the GPL - */ - -#include "linux/sched.h" -#include "asm/uaccess.h" - -int copy_from_user_tt(void *to, const void *from, int n) -{ - if(!access_ok_tt(VERIFY_READ, from, n)) - return(n); - - return(__do_copy_from_user(to, from, n, ¤t->thread.fault_addr, - ¤t->thread.fault_catcher)); -} - -int copy_to_user_tt(void *to, const void *from, int n) -{ - if(!access_ok_tt(VERIFY_WRITE, to, n)) - return(n); - - return(__do_copy_to_user(to, from, n, ¤t->thread.fault_addr, - ¤t->thread.fault_catcher)); -} - -int strncpy_from_user_tt(char *dst, const char *src, int count) -{ - int n; - - if(!access_ok_tt(VERIFY_READ, src, 1)) - return(-EFAULT); - - n = __do_strncpy_from_user(dst, src, count, - ¤t->thread.fault_addr, - ¤t->thread.fault_catcher); - if(n < 0) return(-EFAULT); - return(n); -} - -int __clear_user_tt(void *mem, int len) -{ - return(__do_clear_user(mem, len, - ¤t->thread.fault_addr, - ¤t->thread.fault_catcher)); -} - -int clear_user_tt(void *mem, int len) -{ - if(!access_ok_tt(VERIFY_WRITE, mem, len)) - return(len); - - return(__do_clear_user(mem, len, ¤t->thread.fault_addr, - ¤t->thread.fault_catcher)); -} - -int strnlen_user_tt(const void *str, int len) -{ - return(__do_strnlen_user(str, len, - ¤t->thread.fault_addr, - ¤t->thread.fault_catcher)); -} - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * Emacs will notice this stuff at the end of the file and automatically - * adjust the settings for this buffer only. This must remain at the end - * of the file. - * --------------------------------------------------------------------------- - * Local variables: - * c-file-style: "linux" - * End: - */ diff --git a/arch/um/os-Linux/user_syms.c b/arch/um/os-Linux/user_syms.c deleted file mode 100644 index ef0fb71e4..000000000 --- a/arch/um/os-Linux/user_syms.c +++ /dev/null @@ -1,88 +0,0 @@ -#include "linux/types.h" -#include "linux/module.h" - -/* Some of this are builtin function (some are not but could in the future), - * so I *must* declare good prototypes for them and then EXPORT them. - * The kernel code uses the macro defined by include/linux/string.h, - * so I undef macros; the userspace code does not include that and I - * add an EXPORT for the glibc one.*/ - -#undef strlen -#undef strstr -#undef memcpy -#undef memset - -extern size_t strlen(const char *); -extern void *memcpy(void *, const void *, size_t); -extern void *memset(void *, int, size_t); -extern int printf(const char *, ...); - -EXPORT_SYMBOL(strlen); -EXPORT_SYMBOL(memcpy); -EXPORT_SYMBOL(memset); -EXPORT_SYMBOL(printf); - -EXPORT_SYMBOL(strstr); - -/* Here, instead, I can provide a fake prototype. Yes, someone cares: genksyms. - * However, the modules will use the CRC defined *here*, no matter if it is - * good; so the versions of these symbols will always match - */ -#define EXPORT_SYMBOL_PROTO(sym) \ - int sym(void); \ - EXPORT_SYMBOL(sym); - -EXPORT_SYMBOL_PROTO(__errno_location); - -EXPORT_SYMBOL_PROTO(access); -EXPORT_SYMBOL_PROTO(open); -EXPORT_SYMBOL_PROTO(open64); -EXPORT_SYMBOL_PROTO(close); -EXPORT_SYMBOL_PROTO(read); -EXPORT_SYMBOL_PROTO(write); -EXPORT_SYMBOL_PROTO(dup2); -EXPORT_SYMBOL_PROTO(__xstat); -EXPORT_SYMBOL_PROTO(__lxstat); -EXPORT_SYMBOL_PROTO(__lxstat64); -EXPORT_SYMBOL_PROTO(lseek); -EXPORT_SYMBOL_PROTO(lseek64); -EXPORT_SYMBOL_PROTO(chown); -EXPORT_SYMBOL_PROTO(truncate); -EXPORT_SYMBOL_PROTO(utime); -EXPORT_SYMBOL_PROTO(chmod); -EXPORT_SYMBOL_PROTO(rename); -EXPORT_SYMBOL_PROTO(__xmknod); - -EXPORT_SYMBOL_PROTO(symlink); -EXPORT_SYMBOL_PROTO(link); -EXPORT_SYMBOL_PROTO(unlink); -EXPORT_SYMBOL_PROTO(readlink); - -EXPORT_SYMBOL_PROTO(mkdir); -EXPORT_SYMBOL_PROTO(rmdir); -EXPORT_SYMBOL_PROTO(opendir); -EXPORT_SYMBOL_PROTO(readdir); -EXPORT_SYMBOL_PROTO(closedir); -EXPORT_SYMBOL_PROTO(seekdir); -EXPORT_SYMBOL_PROTO(telldir); - -EXPORT_SYMBOL_PROTO(ioctl); - -EXPORT_SYMBOL_PROTO(pread64); -EXPORT_SYMBOL_PROTO(pwrite64); - -EXPORT_SYMBOL_PROTO(statfs); -EXPORT_SYMBOL_PROTO(statfs64); - -EXPORT_SYMBOL_PROTO(getuid); - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * Emacs will notice this stuff at the end of the file and automatically - * adjust the settings for this buffer only. This must remain at the end - * of the file. - * --------------------------------------------------------------------------- - * Local variables: - * c-file-style: "linux" - * End: - */ diff --git a/arch/x86_64/kernel/domain.c b/arch/x86_64/kernel/domain.c new file mode 100644 index 000000000..0694958c7 --- /dev/null +++ b/arch/x86_64/kernel/domain.c @@ -0,0 +1,93 @@ +#include +#include + +/* Don't do any NUMA setup on Opteron right now. They seem to be + better off with flat scheduling. This is just for SMT. */ + +#ifdef CONFIG_SCHED_SMT + +static struct sched_group sched_group_cpus[NR_CPUS]; +static struct sched_group sched_group_phys[NR_CPUS]; +static DEFINE_PER_CPU(struct sched_domain, cpu_domains); +static DEFINE_PER_CPU(struct sched_domain, phys_domains); +__init void arch_init_sched_domains(void) +{ + int i; + struct sched_group *first = NULL, *last = NULL; + + /* Set up domains */ + for_each_cpu(i) { + struct sched_domain *cpu_domain = &per_cpu(cpu_domains, i); + struct sched_domain *phys_domain = &per_cpu(phys_domains, i); + + *cpu_domain = SD_SIBLING_INIT; + /* Disable SMT NICE for CMP */ + /* RED-PEN use a generic flag */ + if (cpu_data[i].x86_vendor == X86_VENDOR_AMD) + cpu_domain->flags &= ~SD_SHARE_CPUPOWER; + cpu_domain->span = cpu_sibling_map[i]; + cpu_domain->parent = phys_domain; + cpu_domain->groups = &sched_group_cpus[i]; + + *phys_domain = SD_CPU_INIT; + phys_domain->span = cpu_possible_map; + phys_domain->groups = &sched_group_phys[first_cpu(cpu_domain->span)]; + } + + /* Set up CPU (sibling) groups */ + for_each_cpu(i) { + struct sched_domain *cpu_domain = &per_cpu(cpu_domains, i); + int j; + first = last = NULL; + + if (i != first_cpu(cpu_domain->span)) + continue; + + for_each_cpu_mask(j, cpu_domain->span) { + struct sched_group *cpu = &sched_group_cpus[j]; + + cpus_clear(cpu->cpumask); + cpu_set(j, cpu->cpumask); + cpu->cpu_power = SCHED_LOAD_SCALE; + + if (!first) + first = cpu; + if (last) + last->next = cpu; + last = cpu; + } + last->next = first; + } + + first = last = NULL; + /* Set up physical groups */ + for_each_cpu(i) { + struct sched_domain *cpu_domain = &per_cpu(cpu_domains, i); + struct sched_group *cpu = &sched_group_phys[i]; + + if (i != first_cpu(cpu_domain->span)) + continue; + + cpu->cpumask = cpu_domain->span; + /* + * Make each extra sibling increase power by 10% of + * the basic CPU. This is very arbitrary. + */ + cpu->cpu_power = SCHED_LOAD_SCALE + SCHED_LOAD_SCALE*(cpus_weight(cpu->cpumask)-1) / 10; + + if (!first) + first = cpu; + if (last) + last->next = cpu; + last = cpu; + } + last->next = first; + + mb(); + for_each_cpu(i) { + struct sched_domain *cpu_domain = &per_cpu(cpu_domains, i); + cpu_attach_domain(cpu_domain, i); + } +} + +#endif diff --git a/drivers/char/drm/drm_irq.h b/drivers/char/drm/drm_irq.h new file mode 100644 index 000000000..1d1d95116 --- /dev/null +++ b/drivers/char/drm/drm_irq.h @@ -0,0 +1,371 @@ +/** + * \file drm_irq.h + * IRQ support + * + * \author Rickard E. (Rik) Faith + * \author Gareth Hughes + */ + +/* + * Created: Fri Mar 19 14:30:16 1999 by faith@valinux.com + * + * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "drmP.h" + +#include /* For task queue support */ + +#ifndef __HAVE_SHARED_IRQ +#define __HAVE_SHARED_IRQ 0 +#endif + +#if __HAVE_SHARED_IRQ +#define DRM_IRQ_TYPE SA_SHIRQ +#else +#define DRM_IRQ_TYPE 0 +#endif + +/** + * Get interrupt from bus id. + * + * \param inode device inode. + * \param filp file pointer. + * \param cmd command. + * \param arg user argument, pointing to a drm_irq_busid structure. + * \return zero on success or a negative number on failure. + * + * Finds the PCI device with the specified bus id and gets its IRQ number. + * This IOCTL is deprecated, and will now return EINVAL for any busid not equal + * to that of the device that this DRM instance attached to. + */ +int DRM(irq_by_busid)(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_irq_busid_t p; + + if (copy_from_user(&p, (drm_irq_busid_t *)arg, sizeof(p))) + return -EFAULT; + + if ((p.busnum >> 8) != dev->pci_domain || + (p.busnum & 0xff) != dev->pci_bus || + p.devnum != dev->pci_slot || + p.funcnum != dev->pci_func) + return -EINVAL; + + p.irq = dev->irq; + + DRM_DEBUG("%d:%d:%d => IRQ %d\n", + p.busnum, p.devnum, p.funcnum, p.irq); + if (copy_to_user((drm_irq_busid_t *)arg, &p, sizeof(p))) + return -EFAULT; + return 0; +} + +#if __HAVE_IRQ + +/** + * Install IRQ handler. + * + * \param dev DRM device. + * \param irq IRQ number. + * + * Initializes the IRQ related data, and setups drm_device::vbl_queue. Installs the handler, calling the driver + * \c DRM(driver_irq_preinstall)() and \c DRM(driver_irq_postinstall)() functions + * before and after the installation. + */ +int DRM(irq_install)( drm_device_t *dev ) +{ + int ret; + + if ( dev->irq == 0 ) + return -EINVAL; + + down( &dev->struct_sem ); + + /* Driver must have been initialized */ + if ( !dev->dev_private ) { + up( &dev->struct_sem ); + return -EINVAL; + } + + if ( dev->irq_enabled ) { + up( &dev->struct_sem ); + return -EBUSY; + } + dev->irq_enabled = 1; + up( &dev->struct_sem ); + + DRM_DEBUG( "%s: irq=%d\n", __FUNCTION__, dev->irq ); + +#if __HAVE_DMA + dev->dma->next_buffer = NULL; + dev->dma->next_queue = NULL; + dev->dma->this_buffer = NULL; +#endif + +#if __HAVE_IRQ_BH + INIT_WORK(&dev->work, DRM(irq_immediate_bh), dev); +#endif + +#if __HAVE_VBL_IRQ + init_waitqueue_head(&dev->vbl_queue); + + spin_lock_init( &dev->vbl_lock ); + + INIT_LIST_HEAD( &dev->vbl_sigs.head ); + + dev->vbl_pending = 0; +#endif + + /* Before installing handler */ + DRM(driver_irq_preinstall)(dev); + + /* Install handler */ + ret = request_irq( dev->irq, DRM(irq_handler), + DRM_IRQ_TYPE, dev->devname, dev ); + if ( ret < 0 ) { + down( &dev->struct_sem ); + dev->irq_enabled = 0; + up( &dev->struct_sem ); + return ret; + } + + /* After installing handler */ + DRM(driver_irq_postinstall)(dev); + + return 0; +} + +/** + * Uninstall the IRQ handler. + * + * \param dev DRM device. + * + * Calls the driver's \c DRM(driver_irq_uninstall)() function, and stops the irq. + */ +int DRM(irq_uninstall)( drm_device_t *dev ) +{ + int irq_enabled; + + down( &dev->struct_sem ); + irq_enabled = dev->irq_enabled; + dev->irq_enabled = 0; + up( &dev->struct_sem ); + + if ( !irq_enabled ) + return -EINVAL; + + DRM_DEBUG( "%s: irq=%d\n", __FUNCTION__, dev->irq ); + + DRM(driver_irq_uninstall)( dev ); + + free_irq( dev->irq, dev ); + + return 0; +} + +/** + * IRQ control ioctl. + * + * \param inode device inode. + * \param filp file pointer. + * \param cmd command. + * \param arg user argument, pointing to a drm_control structure. + * \return zero on success or a negative number on failure. + * + * Calls irq_install() or irq_uninstall() according to \p arg. + */ +int DRM(control)( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_control_t ctl; + + if ( copy_from_user( &ctl, (drm_control_t *)arg, sizeof(ctl) ) ) + return -EFAULT; + + switch ( ctl.func ) { + case DRM_INST_HANDLER: + if (dev->if_version < DRM_IF_VERSION(1, 2) && + ctl.irq != dev->irq) + return -EINVAL; + return DRM(irq_install)( dev ); + case DRM_UNINST_HANDLER: + return DRM(irq_uninstall)( dev ); + default: + return -EINVAL; + } +} + +#if __HAVE_VBL_IRQ + +/** + * Wait for VBLANK. + * + * \param inode device inode. + * \param filp file pointer. + * \param cmd command. + * \param data user argument, pointing to a drm_wait_vblank structure. + * \return zero on success or a negative number on failure. + * + * Verifies the IRQ is installed. + * + * If a signal is requested checks if this task has already scheduled the same signal + * for the same vblank sequence number - nothing to be done in + * that case. If the number of tasks waiting for the interrupt exceeds 100 the + * function fails. Otherwise adds a new entry to drm_device::vbl_sigs for this + * task. + * + * If a signal is not requested, then calls vblank_wait(). + */ +int DRM(wait_vblank)( DRM_IOCTL_ARGS ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_wait_vblank_t vblwait; + struct timeval now; + int ret = 0; + unsigned int flags; + + if (!dev->irq) + return -EINVAL; + + DRM_COPY_FROM_USER_IOCTL( vblwait, (drm_wait_vblank_t *)data, + sizeof(vblwait) ); + + switch ( vblwait.request.type & ~_DRM_VBLANK_FLAGS_MASK ) { + case _DRM_VBLANK_RELATIVE: + vblwait.request.sequence += atomic_read( &dev->vbl_received ); + vblwait.request.type &= ~_DRM_VBLANK_RELATIVE; + case _DRM_VBLANK_ABSOLUTE: + break; + default: + return -EINVAL; + } + + flags = vblwait.request.type & _DRM_VBLANK_FLAGS_MASK; + + if ( flags & _DRM_VBLANK_SIGNAL ) { + unsigned long irqflags; + drm_vbl_sig_t *vbl_sig; + + vblwait.reply.sequence = atomic_read( &dev->vbl_received ); + + spin_lock_irqsave( &dev->vbl_lock, irqflags ); + + /* Check if this task has already scheduled the same signal + * for the same vblank sequence number; nothing to be done in + * that case + */ + list_for_each_entry( vbl_sig, &dev->vbl_sigs.head, head ) { + if (vbl_sig->sequence == vblwait.request.sequence + && vbl_sig->info.si_signo == vblwait.request.signal + && vbl_sig->task == current) + { + spin_unlock_irqrestore( &dev->vbl_lock, irqflags ); + goto done; + } + } + + if ( dev->vbl_pending >= 100 ) { + spin_unlock_irqrestore( &dev->vbl_lock, irqflags ); + return -EBUSY; + } + + dev->vbl_pending++; + + spin_unlock_irqrestore( &dev->vbl_lock, irqflags ); + + if ( !( vbl_sig = DRM_MALLOC( sizeof( drm_vbl_sig_t ) ) ) ) { + return -ENOMEM; + } + + memset( (void *)vbl_sig, 0, sizeof(*vbl_sig) ); + + vbl_sig->sequence = vblwait.request.sequence; + vbl_sig->info.si_signo = vblwait.request.signal; + vbl_sig->task = current; + + spin_lock_irqsave( &dev->vbl_lock, irqflags ); + + list_add_tail( (struct list_head *) vbl_sig, &dev->vbl_sigs.head ); + + spin_unlock_irqrestore( &dev->vbl_lock, irqflags ); + } else { + ret = DRM(vblank_wait)( dev, &vblwait.request.sequence ); + + do_gettimeofday( &now ); + vblwait.reply.tval_sec = now.tv_sec; + vblwait.reply.tval_usec = now.tv_usec; + } + +done: + DRM_COPY_TO_USER_IOCTL( (drm_wait_vblank_t *)data, vblwait, + sizeof(vblwait) ); + + return ret; +} + +/** + * Send the VBLANK signals. + * + * \param dev DRM device. + * + * Sends a signal for each task in drm_device::vbl_sigs and empties the list. + * + * If a signal is not requested, then calls vblank_wait(). + */ +void DRM(vbl_send_signals)( drm_device_t *dev ) +{ + struct list_head *list, *tmp; + drm_vbl_sig_t *vbl_sig; + unsigned int vbl_seq = atomic_read( &dev->vbl_received ); + unsigned long flags; + + spin_lock_irqsave( &dev->vbl_lock, flags ); + + list_for_each_safe( list, tmp, &dev->vbl_sigs.head ) { + vbl_sig = list_entry( list, drm_vbl_sig_t, head ); + if ( ( vbl_seq - vbl_sig->sequence ) <= (1<<23) ) { + vbl_sig->info.si_code = vbl_seq; + send_sig_info( vbl_sig->info.si_signo, &vbl_sig->info, vbl_sig->task ); + + list_del( list ); + + DRM_FREE( vbl_sig, sizeof(*vbl_sig) ); + + dev->vbl_pending--; + } + } + + spin_unlock_irqrestore( &dev->vbl_lock, flags ); +} + +#endif /* __HAVE_VBL_IRQ */ + +#endif /* __HAVE_IRQ */ diff --git a/drivers/char/drm/drm_pciids.h b/drivers/char/drm/drm_pciids.h new file mode 100644 index 000000000..3b5f8d3a2 --- /dev/null +++ b/drivers/char/drm/drm_pciids.h @@ -0,0 +1,203 @@ +/* + This file is auto-generated from the drm_pciids.txt in the DRM CVS + Please contact dri-devel@lists.sf.net to add new cards to this list +*/ +#define radeon_PCI_IDS \ + {0x1002, 0x4136, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x4137, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x4237, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x4242, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x4242, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x4336, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x4337, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x4437, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x4964, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x4965, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x4966, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x4967, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x4C57, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x4C58, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x4C59, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x4C5A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x4C64, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x4C65, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x4C66, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x4C67, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x5144, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x5145, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x5146, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x5147, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x5148, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x5149, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x514A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x514B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x514C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x514D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x514E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x514F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x5157, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x5158, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x5159, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x515A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x5168, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x5169, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x516A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x516B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x516C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x5834, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x5835, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x5836, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x5837, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x5960, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x5961, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x5962, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x5963, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x5964, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x5968, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x5969, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x596A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x596B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x5c61, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x5c62, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x5c63, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x5c64, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0, 0, 0} + +#define r128_PCI_IDS \ + {0x1002, 0x4c45, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x4c46, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x4d46, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x4d4c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x5041, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x5042, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x5043, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x5044, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x5045, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x5046, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x5047, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x5048, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x5049, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x504A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x504B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x504C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x504D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x504E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x504F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x5050, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x5051, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x5052, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x5053, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x5054, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x5055, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x5056, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x5057, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x5058, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x5245, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x5246, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x5247, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x524b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x524c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x534d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x5446, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x544C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x5452, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0, 0, 0} + +#define mga_PCI_IDS \ + {0x102b, 0x0521, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x102b, 0x0525, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x102b, 0x2527, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0, 0, 0} + +#define mach64_PCI_IDS \ + {0x1002, 0x4749, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x4750, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x4751, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x4742, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x4744, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x4c49, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x4c50, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x4c51, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x4c42, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x4c44, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x474c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x474f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x4752, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x4753, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x474d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x474e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x4c52, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x4c53, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x4c4d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1002, 0x4c4e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0, 0, 0} + +#define sisdrv_PCI_IDS \ + {0x1039, 0x0300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1039, 0x5300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1039, 0x6300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1039, 0x7300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0, 0, 0} + +#define tdfx_PCI_IDS \ + {0x121a, 0x0003, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x121a, 0x0004, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x121a, 0x0005, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x121a, 0x0007, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x121a, 0x0009, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x121a, 0x000b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0, 0, 0} + +#define viadrv_PCI_IDS \ + {0x1106, 0x3022, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1106, 0x3122, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1106, 0x7205, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1106, 0x7204, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0, 0, 0} + +#define i810_PCI_IDS \ + {0x8086, 0x7121, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x8086, 0x7123, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x8086, 0x7125, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x8086, 0x1132, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0, 0, 0} + +#define i830_PCI_IDS \ + {0x8086, 0x3577, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x8086, 0x2562, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x8086, 0x3582, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x8086, 0x2572, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0, 0, 0} + +#define gamma_PCI_IDS \ + {0x3d3d, 0x0008, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0, 0, 0} + +#define savage_PCI_IDS \ + {0x5333, 0x8a22, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x5333, 0x8a23, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x5333, 0x8c10, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x5333, 0x8c11, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x5333, 0x8c12, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x5333, 0x8c13, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x5333, 0x8c20, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x5333, 0x8c21, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x5333, 0x8c22, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x5333, 0x8c24, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x5333, 0x8c26, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x5333, 0x8c2a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x5333, 0x8c2b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x5333, 0x8c2c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x5333, 0x8c2d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x5333, 0x8c2e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x5333, 0x8c2f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x5333, 0x8a25, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x5333, 0x8a26, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x5333, 0x8d01, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x5333, 0x8d02, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x5333, 0x8d04, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0, 0, 0} + +#define ffb_PCI_IDS \ + {0, 0, 0} + diff --git a/drivers/char/dz.c b/drivers/char/dz.c deleted file mode 100644 index 23630030e..000000000 --- a/drivers/char/dz.c +++ /dev/null @@ -1,1540 +0,0 @@ -/* - * dz.c: Serial port driver for DECStations equiped - * with the DZ chipset. - * - * Copyright (C) 1998 Olivier A. D. Lebaillif - * - * Email: olivier.lebaillif@ifrsys.com - * - * [31-AUG-98] triemer - * Changed IRQ to use Harald's dec internals interrupts.h - * removed base_addr code - moving address assignment to setup.c - * Changed name of dz_init to rs_init to be consistent with tc code - * [13-NOV-98] triemer fixed code to receive characters - * after patches by harald to irq code. - * [09-JAN-99] triemer minor fix for schedule - due to removal of timeout - * field from "current" - somewhere between 2.1.121 and 2.1.131 -Qua Jun 27 15:02:26 BRT 2001 - * [27-JUN-2001] Arnaldo Carvalho de Melo - cleanups - * - * Parts (C) 1999 David Airlie, airlied@linux.ie - * [07-SEP-99] Bugfixes - */ - -/* #define DEBUG_DZ 1 */ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include /* for definition of SERIAL */ - -/* for definition of struct console */ -#ifdef CONFIG_SERIAL_CONSOLE -#define CONSOLE_LINE (3) -#endif /* ifdef CONFIG_SERIAL_CONSOLE */ -#if defined(CONFIG_SERIAL_CONSOLE) || defined(DEBUG_DZ) -#include -#endif /* if defined(CONFIG_SERIAL_CONSOLE) || defined(DEBUG_DZ) */ - -#include -#include - -#include -#include -#include -#include -#include - -#ifdef DEBUG_DZ -#include -#include -#include - -extern int (*prom_printf) (char *,...); -#endif - - - -#include "dz.h" - -#define DZ_INTR_DEBUG 1 - -DECLARE_TASK_QUEUE(tq_serial); - -static struct dz_serial *lines[4]; -static unsigned char tmp_buffer[256]; - - - -#ifdef DEBUG_DZ -/* - * debugging code to send out chars via prom - */ -static void debug_console( const char *s,int count) -{ - unsigned i; - - for (i = 0; i < count; i++) { - if (*s == 10) - prom_printf("%c", 13); - prom_printf("%c", *s++); - } -} -#endif - -/* - * ------------------------------------------------------------ - * dz_in () and dz_out () - * - * These routines are used to access the registers of the DZ - * chip, hiding relocation differences between implementation. - * ------------------------------------------------------------ - */ - -static inline unsigned short dz_in (struct dz_serial *info, unsigned offset) -{ - volatile u16 *addr = (volatile u16 *)(info->port + offset); - - return *addr; -} - -static inline void dz_out (struct dz_serial *info, unsigned offset, - unsigned short value) -{ - volatile u16 *addr = (volatile u16 *)(info->port + offset); - *addr = value; -} - -/* - * ------------------------------------------------------------ - * rs_stop () and rs_start () - * - * These routines are called before setting or resetting - * tty->stopped. They enable or disable transmitter interrupts, - * as necessary. - * ------------------------------------------------------------ - */ - -static void dz_stop (struct tty_struct *tty) -{ - struct dz_serial *info; - unsigned short mask, tmp; - - if (!tty) - return; - - info = (struct dz_serial *)tty->driver_data; - - mask = 1 << info->line; - tmp = dz_in (info, DZ_TCR); /* read the TX flag */ - - tmp &= ~mask; /* clear the TX flag */ - dz_out (info, DZ_TCR, tmp); -} - -static void dz_start (struct tty_struct *tty) -{ - struct dz_serial *info = (struct dz_serial *)tty->driver_data; - unsigned short mask, tmp; - - mask = 1 << info->line; - tmp = dz_in (info, DZ_TCR); /* read the TX flag */ - - tmp |= mask; /* set the TX flag */ - dz_out (info, DZ_TCR, tmp); -} - -/* - * ------------------------------------------------------------ - * Here starts the interrupt handling routines. All of the - * following subroutines are declared as inline and are folded - * into dz_interrupt. They were separated out for readability's - * sake. - * - * Note: rs_interrupt() is a "fast" interrupt, which means that it - * runs with interrupts turned off. People who may want to modify - * rs_interrupt() should try to keep the interrupt handler as fast as - * possible. After you are done making modifications, it is not a bad - * idea to do: - * - * gcc -S -DKERNEL -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer dz.c - * - * and look at the resulting assemble code in serial.s. - * - * ------------------------------------------------------------ - */ - -/* - * ------------------------------------------------------------ - * dz_sched_event () - * - * This routine is used by the interrupt handler to schedule - * processing in the software interrupt portion of the driver. - * ------------------------------------------------------------ - */ -static inline void dz_sched_event (struct dz_serial *info, int event) -{ - info->event |= 1 << event; - queue_task(&info->tqueue, &tq_serial); - mark_bh(SERIAL_BH); -} - -/* - * ------------------------------------------------------------ - * receive_char () - * - * This routine deals with inputs from any lines. - * ------------------------------------------------------------ - */ -static inline void receive_chars (struct dz_serial *info_in) -{ - struct dz_serial *info; - struct tty_struct *tty = 0; - struct async_icount *icount; - int ignore = 0; - unsigned short status, tmp; - unsigned char ch; - - /* - * This code is going to be a problem... the call to tty_flip_buffer - * is going to need to be rethought... - */ - do { - status = dz_in (info_in, DZ_RBUF); - info = lines[LINE(status)]; - - /* punt so we don't get duplicate characters */ - if (!(status & DZ_DVAL)) - goto ignore_char; - - ch = UCHAR(status); /* grab the char */ - -#if 0 - if (info->is_console) { - if (ch == 0) - return; /* it's a break ... */ - } -#endif - - tty = info->tty; /* now tty points to the proper dev */ - icount = &info->icount; - - if (!tty) - break; - if (tty->flip.count >= TTY_FLIPBUF_SIZE) break; - - *tty->flip.char_buf_ptr = ch; - *tty->flip.flag_buf_ptr = 0; - icount->rx++; - - /* keep track of the statistics */ - if (status & (DZ_OERR | DZ_FERR | DZ_PERR)) { - if (status & DZ_PERR) /* parity error */ - icount->parity++; - else if (status & DZ_FERR) /* frame error */ - icount->frame++; - if (status & DZ_OERR) /* overrun error */ - icount->overrun++; - - /* - * Check to see if we should ignore the character and - * mask off conditions that should be ignored - */ - - if (status & info->ignore_status_mask) { - if (++ignore > 100) - break; - goto ignore_char; - } - - /* mask off the error conditions we want to ignore */ - tmp = status & info->read_status_mask; - - if (tmp & DZ_PERR) { - *tty->flip.flag_buf_ptr = TTY_PARITY; -#ifdef DEBUG_DZ - debug_console("PERR\n",5); -#endif /* DEBUG_DZ */ - } else if (tmp & DZ_FERR) { - *tty->flip.flag_buf_ptr = TTY_FRAME; -#ifdef DEBUG_DZ - debug_console("FERR\n",5); -#endif /* DEBUG_DZ */ - } if (tmp & DZ_OERR) { -#ifdef DEBUG_DZ - debug_console("OERR\n",5); -#endif /* DEBUG_DZ */ - if (tty->flip.count < TTY_FLIPBUF_SIZE) { - tty->flip.count++; - tty->flip.flag_buf_ptr++; - tty->flip.char_buf_ptr++; - *tty->flip.flag_buf_ptr = TTY_OVERRUN; - } - } - } - tty->flip.flag_buf_ptr++; - tty->flip.char_buf_ptr++; - tty->flip.count++; -ignore_char: - ; - } while (status & DZ_DVAL); - - if (tty) - tty_flip_buffer_push(tty); -} - -/* - * ------------------------------------------------------------ - * transmit_char () - * - * This routine deals with outputs to any lines. - * ------------------------------------------------------------ - */ -static inline void transmit_chars (struct dz_serial *info) -{ - unsigned char tmp; - - if (info->x_char) { /* XON/XOFF chars */ - dz_out(info, DZ_TDR, info->x_char); - info->icount.tx++; - info->x_char = 0; - return; - } - - /* if nothing to do or stopped or hardware stopped */ - if ((info->xmit_cnt <= 0) || info->tty->stopped || - info->tty->hw_stopped) { - dz_stop(info->tty); - return; - } - - /* - * If something to do ... (rember the dz has no output fifo so we go - * one char at a time :-< - */ - tmp = (unsigned short) info->xmit_buf[info->xmit_tail++]; - dz_out(info, DZ_TDR, tmp); - info->xmit_tail = info->xmit_tail & (DZ_XMIT_SIZE - 1); - info->icount.tx++; - - if (--info->xmit_cnt < WAKEUP_CHARS) - dz_sched_event(info, DZ_EVENT_WRITE_WAKEUP); - - /* Are we done */ - if (info->xmit_cnt <= 0) - dz_stop(info->tty); -} - -/* - * ------------------------------------------------------------ - * check_modem_status () - * - * Only valid for the MODEM line duh ! - * ------------------------------------------------------------ - */ -static inline void check_modem_status (struct dz_serial *info) -{ - unsigned short status; - - /* if not ne modem line just return */ - if (info->line != DZ_MODEM) - return; - - status = dz_in(info, DZ_MSR); - - /* it's easy, since DSR2 is the only bit in the register */ - if (status) - info->icount.dsr++; -} - -/* - * ------------------------------------------------------------ - * dz_interrupt () - * - * this is the main interrupt routine for the DZ chip. - * It deals with the multiple ports. - * ------------------------------------------------------------ - */ -static void dz_interrupt (int irq, void *dev, struct pt_regs *regs) -{ - struct dz_serial *info; - unsigned short status; - - /* get the reason why we just got an irq */ - status = dz_in((struct dz_serial *)dev, DZ_CSR); - info = lines[LINE(status)]; /* re-arrange info the proper port */ - - if (status & DZ_RDONE) - receive_chars(info); /* the receive function */ - - if (status & DZ_TRDY) - transmit_chars (info); -} - -/* - * ------------------------------------------------------------------- - * Here ends the DZ interrupt routines. - * ------------------------------------------------------------------- - */ - -/* - * 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_data) -{ - struct dz_serial *info = (struct dz_serial *) private_data; - struct tty_struct *tty = info->tty; - - if (!tty) - return; - - if (test_and_clear_bit(DZ_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); - } -} - -/* - * ------------------------------------------------------------------- - * This routine is called from the scheduler tqueue when the interrupt - * routine has signalled that a hangup has occurred. The path of - * hangup processing is: - * - * serial interrupt routine -> (scheduler tqueue) -> - * do_serial_hangup() -> tty->hangup() -> rs_hangup() - * ------------------------------------------------------------------- - */ -static void do_serial_hangup (void *private_data) -{ - struct dz_serial *info = (struct dz_serial *) private_data; - struct tty_struct *tty = info->tty; - - if (!tty) - return; - - tty_hangup(tty); -} - -/* - * ------------------------------------------------------------------- - * startup () - * - * various initialization tasks - * ------------------------------------------------------------------- - */ -static int startup (struct dz_serial *info) -{ - unsigned long page, flags; - unsigned short tmp; - - if (info->is_initialized) - return 0; - - save_and_cli(flags); - - if (!info->port) { - if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags); - restore_flags(flags); - return -ENODEV; - } - - if (!info->xmit_buf) { - page = get_zeroed_page(GFP_KERNEL); - if (!page) { - restore_flags (flags); - return -ENOMEM; - } - info->xmit_buf = (unsigned char *)page; - } - - if (info->tty) - clear_bit(TTY_IO_ERROR, &info->tty->flags); - - /* enable the interrupt and the scanning */ - tmp = dz_in(info, DZ_CSR); - tmp |= (DZ_RIE | DZ_TIE | DZ_MSE); - dz_out(info, DZ_CSR, tmp); - - info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; - - change_speed(info); /* set up the speed */ - - /* - * Clear the line transmitter buffer I can't figure out why I need to - * do this - but its necessary - in order for the console portion and - * the interrupt portion to live happily side by side. - */ - - info->is_initialized = 1; - - restore_flags(flags); - - return 0; -} - -/* - * ------------------------------------------------------------------- - * shutdown () - * - * 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 dz_serial *info) -{ - unsigned long flags; - unsigned short tmp; - - if (!info->is_initialized) - return; - - save_and_cli(flags); - - dz_stop (info->tty); - - info->cflags &= ~DZ_CREAD; /* turn off receive enable flag */ - dz_out(info, DZ_LPR, info->cflags); - - if (info->xmit_buf) { /* free Tx buffer */ - free_page((unsigned long)info->xmit_buf); - info->xmit_buf = 0; - } - - if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) { - tmp = dz_in(info, DZ_TCR); - if (tmp & DZ_MODEM_DTR) { - tmp &= ~DZ_MODEM_DTR; - dz_out(info, DZ_TCR, tmp); - } - } - - if (info->tty) - set_bit (TTY_IO_ERROR, &info->tty->flags); - - info->is_initialized = 0; - - restore_flags (flags); -} - -/* - * ------------------------------------------------------------------- - * change_speed () - * - * set the baud rate. - * ------------------------------------------------------------------- - */ -static void change_speed (struct dz_serial *info) -{ - unsigned long flags; - unsigned cflag; - int baud; - - if (!info->tty || !info->tty->termios) - return; - - save_and_cli(flags); - - info->cflags = info->line; - - cflag = info->tty->termios->c_cflag; - - switch (cflag & CSIZE) { - case CS5: - info->cflags |= DZ_CS5; - break; - case CS6: - info->cflags |= DZ_CS6; - break; - case CS7: - info->cflags |= DZ_CS7; - break; - case CS8: - default: - info->cflags |= DZ_CS8; - } - - if (cflag & CSTOPB) - info->cflags |= DZ_CSTOPB; - if (cflag & PARENB) - info->cflags |= DZ_PARENB; - if (cflag & PARODD) - info->cflags |= DZ_PARODD; - - baud = tty_get_baud_rate(info->tty); - switch (baud) { - case 50: - info->cflags |= DZ_B50; - break; - case 75: - info->cflags |= DZ_B75; - break; - case 110: - info->cflags |= DZ_B110; - break; - case 134: - info->cflags |= DZ_B134; - break; - case 150: - info->cflags |= DZ_B150; - break; - case 300: - info->cflags |= DZ_B300; - break; - case 600: - info->cflags |= DZ_B600; - break; - case 1200: - info->cflags |= DZ_B1200; - break; - case 1800: - info->cflags |= DZ_B1800; - break; - case 2000: - info->cflags |= DZ_B2000; - break; - case 2400: - info->cflags |= DZ_B2400; - break; - case 3600: - info->cflags |= DZ_B3600; - break; - case 4800: - info->cflags |= DZ_B4800; - break; - case 7200: - info->cflags |= DZ_B7200; - break; - case 9600: - default: - info->cflags |= DZ_B9600; - } - - info->cflags |= DZ_RXENAB; - dz_out(info, DZ_LPR, info->cflags); - - /* setup accept flag */ - info->read_status_mask = DZ_OERR; - if (I_INPCK(info->tty)) - info->read_status_mask |= (DZ_FERR | DZ_PERR); - - /* characters to ignore */ - info->ignore_status_mask = 0; - if (I_IGNPAR(info->tty)) - info->ignore_status_mask |= (DZ_FERR | DZ_PERR); - - restore_flags(flags); -} - -/* - * ------------------------------------------------------------------- - * dz_flush_char () - * - * Flush the buffer. - * ------------------------------------------------------------------- - */ -static void dz_flush_chars (struct tty_struct *tty) -{ - struct dz_serial *info = (struct dz_serial *)tty->driver_data; - unsigned long flags; - - if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped || - !info->xmit_buf) - return; - - save_and_cli(flags); - dz_start (info->tty); - restore_flags(flags); -} - - -/* - * ------------------------------------------------------------------- - * dz_write () - * - * main output routine. - * ------------------------------------------------------------------- - */ -static int dz_write (struct tty_struct *tty, int from_user, - const unsigned char *buf, int count) -{ - struct dz_serial *info = (struct dz_serial *)tty->driver_data; - unsigned long flags; - int c, ret = 0; - - if (!tty ) - return ret; - if (!info->xmit_buf) - return ret; - if (!tmp_buf) - tmp_buf = tmp_buffer; - - if (from_user) { - down (&tmp_buf_sem); - while (1) { - c = MIN(count, MIN(DZ_XMIT_SIZE - info->xmit_cnt - 1, - DZ_XMIT_SIZE - info->xmit_head)); - if (c <= 0) - break; - - c -= copy_from_user (tmp_buf, buf, c); - if (!c) { - if (!ret) - ret = -EFAULT; - break; - } - - save_and_cli(flags); - - c = MIN(c, MIN(DZ_XMIT_SIZE - info->xmit_cnt - 1, - DZ_XMIT_SIZE - info->xmit_head)); - memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c); - info->xmit_head = ((info->xmit_head + c) & - (DZ_XMIT_SIZE - 1)); - info->xmit_cnt += c; - restore_flags(flags); - - buf += c; - count -= c; - ret += c; - } - up(&tmp_buf_sem); - } else { - while (1) { - save_and_cli(flags); - - c = MIN(count, MIN(DZ_XMIT_SIZE - info->xmit_cnt - 1, - DZ_XMIT_SIZE - info->xmit_head)); - if (c <= 0) { - restore_flags (flags); - break; - } - memcpy(info->xmit_buf + info->xmit_head, buf, c); - info->xmit_head = ((info->xmit_head + c) & - (DZ_XMIT_SIZE-1)); - info->xmit_cnt += c; - restore_flags(flags); - - buf += c; - count -= c; - ret += c; - } - } - - if (info->xmit_cnt) { - if (!tty->stopped) { - if (!tty->hw_stopped) { - dz_start (info->tty); - } - } - } - - return ret; -} - -/* - * ------------------------------------------------------------------- - * dz_write_room () - * - * compute the amount of space available for writing. - * ------------------------------------------------------------------- - */ -static int dz_write_room (struct tty_struct *tty) -{ - struct dz_serial *info = (struct dz_serial *)tty->driver_data; - int ret; - - ret = DZ_XMIT_SIZE - info->xmit_cnt - 1; - if (ret < 0) - ret = 0; - - return ret; -} - -/* - * ------------------------------------------------------------------- - * dz_chars_in_buffer () - * - * compute the amount of char left to be transmitted - * ------------------------------------------------------------------- - */ -static int dz_chars_in_buffer (struct tty_struct *tty) -{ - struct dz_serial *info = (struct dz_serial *)tty->driver_data; - - return info->xmit_cnt; -} - -/* - * ------------------------------------------------------------------- - * dz_flush_buffer () - * - * Empty the output buffer - * ------------------------------------------------------------------- - */ -static void dz_flush_buffer (struct tty_struct *tty) -{ - struct dz_serial *info = (struct dz_serial *)tty->driver_data; - - cli(); - info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; - sti(); - - wake_up_interruptible (&tty->write_wait); - - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - tty->ldisc.write_wakeup(tty); -} - -/* - * ------------------------------------------------------------ - * dz_throttle () and dz_unthrottle () - * - * This routine is called by the upper-layer tty layer to signal that - * incoming characters should be throttled (or not). - * ------------------------------------------------------------ - */ -static void dz_throttle (struct tty_struct *tty) -{ - struct dz_serial *info = (struct dz_serial *)tty->driver_data; - - if (I_IXOFF(tty)) - info->x_char = STOP_CHAR(tty); -} - -static void dz_unthrottle (struct tty_struct *tty) -{ - struct dz_serial *info = (struct dz_serial *)tty->driver_data; - - if (I_IXOFF(tty)) { - if (info->x_char) - info->x_char = 0; - else - info->x_char = START_CHAR(tty); - } -} - -static void dz_send_xchar (struct tty_struct *tty, char ch) -{ - struct dz_serial *info = (struct dz_serial *)tty->driver_data; - - info->x_char = ch; - - if (ch) - dz_start(info->tty); -} - -/* - * ------------------------------------------------------------ - * rs_ioctl () and friends - * ------------------------------------------------------------ - */ -static int get_serial_info(struct dz_serial *info, - struct serial_struct *retinfo) -{ - struct serial_struct tmp; - - if (!retinfo) - return -EFAULT; - - memset (&tmp, 0, sizeof(tmp)); - - tmp.type = info->type; - tmp.line = info->line; - tmp.port = info->port; - tmp.irq = SERIAL; - tmp.flags = info->flags; - tmp.baud_base = info->baud_base; - tmp.close_delay = info->close_delay; - tmp.closing_wait = info->closing_wait; - - return copy_to_user(retinfo, &tmp, sizeof(*retinfo)) ? -EFAULT : 0; -} - -static int set_serial_info (struct dz_serial *info, - struct serial_struct *new_info) -{ - struct serial_struct new_serial; - struct dz_serial old_info; - int retval = 0; - - if (!new_info) - return -EFAULT; - - if (copy_from_user(&new_serial, new_info, sizeof(new_serial))) - return -EFAULT; - - old_info = *info; - - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - - if (info->count > 1) - return -EBUSY; - - /* - * 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->type = new_serial.type; - info->close_delay = new_serial.close_delay; - info->closing_wait = new_serial.closing_wait; - - retval = startup(info); - - return retval; -} - -/* - * get_lsr_info - get line status register info - * - * Purpose: Let user call ioctl() to get info when the UART physically - * is emptied. On bus types like RS485, the transmitter must - * release the bus after transmitting. This must be done when - * the transmit shift register is empty, not be done when the - * transmit holding register is empty. This functionality - * allows an RS485 driver to be written in user space. - */ -static int get_lsr_info (struct dz_serial *info, unsigned int *value) -{ - unsigned short status = dz_in (info, DZ_LPR); - - return put_user (status, value); -} - -/* - * This routine sends a break character out the serial port. - */ -static void send_break (struct dz_serial *info, int duration) -{ - unsigned long flags; - unsigned short tmp, mask; - - if (!info->port) - return; - - mask = 1 << info->line; - tmp = dz_in (info, DZ_TCR); - tmp |= mask; - - current->state = TASK_INTERRUPTIBLE; - - save_and_cli(flags); - dz_out(info, DZ_TCR, tmp); - schedule_timeout(duration); - tmp &= ~mask; - dz_out(info, DZ_TCR, tmp); - restore_flags(flags); -} - -static int dz_ioctl(struct tty_struct *tty, struct file *file, - unsigned int cmd, unsigned long arg) -{ - int error; - struct dz_serial * info = (struct dz_serial *)tty->driver_data; - int retval; - - if (cmd != TIOCGSERIAL && cmd != TIOCSSERIAL && - cmd != TIOCSERCONFIG && cmd != TIOCSERGWILD && - cmd != TIOCSERSWILD && cmd != TIOCSERGSTRUCT) { - if (tty->flags & (1 << TTY_IO_ERROR)) - return -EIO; - } - - switch (cmd) { - case TCSBRK: /* SVID version: non-zero arg --> no break */ - retval = tty_check_change(tty); - if (retval) - return retval; - tty_wait_until_sent(tty, 0); - if (!arg) - send_break(info, HZ/4); /* 1/4 second */ - return 0; - - case TCSBRKP: /* support for POSIX tcsendbreak() */ - retval = tty_check_change(tty); - if (retval) - return retval; - tty_wait_until_sent(tty, 0); - send_break(info, arg ? arg*(HZ/10) : HZ/4); - return 0; - - case TIOCGSOFTCAR: - return put_user(C_CLOCAL(tty) ? 1 : 0, (unsigned long *)arg); - - case TIOCSSOFTCAR: - if (get_user (arg, (unsigned long *)arg)) - return -EFAULT; - - tty->termios->c_cflag = (tty->termios->c_cflag & ~CLOCAL) | - (arg ? CLOCAL : 0); - return 0; - - case TIOCGSERIAL: - return get_serial_info(info, (struct serial_struct *)arg); - - case TIOCSSERIAL: - return set_serial_info(info, (struct serial_struct *) arg); - - case TIOCSERGETLSR: /* Get line status register */ - return get_lsr_info (info, (unsigned int *)arg); - - case TIOCSERGSTRUCT: - return copy_to_user((struct dz_serial *)arg, info, - sizeof(struct dz_serial)) ? -EFAULT : 0; - - default: - return -ENOIOCTLCMD; - } - - return 0; -} - -static void dz_set_termios (struct tty_struct *tty, - struct termios *old_termios) -{ - struct dz_serial *info = (struct dz_serial *)tty->driver_data; - - if (tty->termios->c_cflag == old_termios->c_cflag) - return; - - change_speed (info); - - if ((old_termios->c_cflag & CRTSCTS) && - !(tty->termios->c_cflag & CRTSCTS)) { - tty->hw_stopped = 0; - dz_start(tty); - } -} - -/* - * ------------------------------------------------------------ - * dz_close() - * - * This routine is called when the serial port gets closed. First, we - * wait for the last remaining data to be sent. Then, we turn off - * the transmit enable and receive enable flags. - * ------------------------------------------------------------ - */ -static void dz_close(struct tty_struct *tty, struct file *filp) -{ - struct dz_serial * info = (struct dz_serial *)tty->driver_data; - unsigned long flags; - - if (!info) - return; - - save_and_cli(flags); - - if (tty_hung_up_p(filp)) { - restore_flags(flags); - return; - } - - 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("dz_close: bad serial port count; tty->count is 1, " - "info->count is %d\n", info->count); - info->count = 1; - } - - if (--info->count < 0) { - printk("ds_close: bad serial port count for ttyS%02d: %d\n", - info->line, info->count); - info->count = 0; - } - - if (info->count) { - restore_flags(flags); - return; - } - info->flags |= DZ_CLOSING; - /* - * 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 != DZ_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. - */ - 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 (tty->ldisc.num != ldiscs[N_TTY].num) { - if (tty->ldisc.close) - tty->ldisc.close(tty); - tty->ldisc = ldiscs[N_TTY]; - tty->termios->c_line = N_TTY; - if (tty->ldisc.open) - tty->ldisc.open(tty); - } - 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 &= ~(DZ_NORMAL_ACTIVE | DZ_CLOSING); - wake_up_interruptible(&info->close_wait); - - restore_flags(flags); -} - -/* - * dz_hangup () --- called by tty_hangup() when a hangup is signaled. - */ -static void dz_hangup (struct tty_struct *tty) -{ - struct dz_serial *info = (struct dz_serial *) tty->driver_data; - - dz_flush_buffer(tty); - shutdown(info); - info->event = 0; - info->count = 0; - info->flags &= ~DZ_NORMAL_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 dz_serial *info) -{ - DECLARE_WAITQUEUE(wait, current); - int retval; - int do_clocal = 0; - - /* - * If the device is in the middle of being closed, then block - * until it's done, and then try again. - */ - if (info->flags & DZ_CLOSING) { - interruptible_sleep_on(&info->close_wait); - return -EAGAIN; - } - - /* - * 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))) { - info->flags |= DZ_NORMAL_ACTIVE; - - return 0; - } - - 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 dz_close() knows when to free - * things. We restore it upon exit, either normal or abnormal. - */ - retval = 0; - add_wait_queue(&info->open_wait, &wait); - - info->count--; - info->blocked_open++; - while (1) { - set_current_state(TASK_INTERRUPTIBLE); - if (tty_hung_up_p (filp) || !(info->is_initialized)) { - retval = -EAGAIN; - break; - } - if (!(info->flags & DZ_CLOSING) && do_clocal) - break; - if (signal_pending(current)) { - retval = -ERESTARTSYS; - break; - } - schedule(); - } - - current->state = TASK_RUNNING; - remove_wait_queue (&info->open_wait, &wait); - if (!tty_hung_up_p(filp)) - info->count++; - info->blocked_open--; - - if (retval) - return retval; - info->flags |= DZ_NORMAL_ACTIVE; - return 0; -} - -/* - * This routine is called whenever a serial port is opened. It - * enables interrupts for a serial port. It also performs the - * serial-specific initialization for the tty structure. - */ -static int dz_open (struct tty_struct *tty, struct file *filp) -{ - struct dz_serial *info; - int retval, line; - - line = tty->index; - - /* - * The dz lines for the mouse/keyboard must be opened using their - * respective drivers. - */ - if ((line < 0) || (line >= DZ_NB_PORT)) - return -ENODEV; - - if ((line == DZ_KEYBOARD) || (line == DZ_MOUSE)) - return -ENODEV; - - info = lines[line]; - info->count++; - - tty->driver_data = info; - info->tty = tty; - - /* - * Start up serial port - */ - retval = startup (info); - if (retval) - return retval; - - retval = block_til_ready (tty, filp, info); - if (retval) - return retval; - - return 0; -} - -static void show_serial_version (void) -{ - printk("%s%s\n", dz_name, dz_version); -} - -static struct tty_driver *serial_driver; - -static struct tty_operations serial_ops = { - .open = dz_open, - .close = dz_close, - .write = dz_write, - .flush_chars = dz_flush_chars, - .write_room = dz_write_room, - .chars_in_buffer = dz_chars_in_buffer, - .flush_buffer = dz_flush_buffer, - .ioctl = dz_ioctl, - .throttle = dz_throttle, - .unthrottle = dz_unthrottle, - .send_xchar = dz_send_xchar, - .set_termios = dz_set_termios, - .stop = dz_stop, - .start = dz_start, - .hangup = dz_hangup, -}; - -int __init dz_init(void) -{ - int i, flags; - struct dz_serial *info; - - serial_driver = alloc_tty_driver(DZ_NB_PORT); - if (!serial_driver) - return -ENOMEM; - - /* Setup base handler, and timer table. */ - init_bh(SERIAL_BH, do_serial_bh); - - show_serial_version(); - - serial_driver->owner = THIS_MODULE; - serial_driver->devfs_name = "tts/"; - serial_driver->name = "ttyS"; - serial_driver->major = TTY_MAJOR; - serial_driver->minor_start = 64; - 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 | TTY_DRIVER_NO_DEVFS; - tty_set_operations(serial_driver, &serial_ops); - - if (tty_register_driver(serial_driver)) - panic("Couldn't register serial driver\n"); - - save_flags(flags); cli(); - for (i=0; i < DZ_NB_PORT; i++) { - info = &multi[i]; - lines[i] = info; - info->magic = SERIAL_MAGIC; - - if ((mips_machtype == MACH_DS23100) || - (mips_machtype == MACH_DS5100)) - info->port = (unsigned long) KN01_DZ11_BASE; - else - info->port = (unsigned long) KN02_DZ11_BASE; - - info->line = i; - info->tty = 0; - info->close_delay = 50; - info->closing_wait = 3000; - info->x_char = 0; - info->event = 0; - info->count = 0; - info->blocked_open = 0; - info->tqueue.routine = do_softint; - info->tqueue.data = info; - info->tqueue_hangup.routine = do_serial_hangup; - info->tqueue_hangup.data = info; - init_waitqueue_head(&info->open_wait); - init_waitqueue_head(&info->close_wait); - - /* - * If we are pointing to address zero then punt - not correctly - * set up in setup.c to handle this. - */ - if (! info->port) - return 0; - - printk("ttyS%02d at 0x%08x (irq = %d)\n", info->line, - info->port, SERIAL); - - tty_register_device(serial_driver, info->line, NULL); - } - - /* Reset the chip */ -#ifndef CONFIG_SERIAL_CONSOLE - { - int tmp; - dz_out(info, DZ_CSR, DZ_CLR); - while ((tmp = dz_in(info,DZ_CSR)) & DZ_CLR); - wbflush(); - - /* Enable scanning */ - dz_out(info, DZ_CSR, DZ_MSE); - } -#endif - - /* - * Order matters here... the trick is that flags is updated... in - * request_irq - to immediatedly obliterate it is unwise. - */ - restore_flags(flags); - - if (request_irq(SERIAL, dz_interrupt, SA_INTERRUPT, "DZ", lines[0])) - panic("Unable to register DZ interrupt\n"); - - return 0; -} - -#ifdef CONFIG_SERIAL_CONSOLE -static void dz_console_put_char (unsigned char ch) -{ - unsigned long flags; - int loops = 2500; - unsigned short tmp = ch; - /* - * this code sends stuff out to serial device - spinning its wheels and - * waiting. - */ - - /* force the issue - point it at lines[3]*/ - dz_console = &multi[CONSOLE_LINE]; - - save_and_cli(flags); - - /* spin our wheels */ - while (((dz_in(dz_console, DZ_CSR) & DZ_TRDY) != DZ_TRDY) && loops--) - ; - - /* Actually transmit the character. */ - dz_out(dz_console, DZ_TDR, tmp); - - restore_flags(flags); -} - -/* - * ------------------------------------------------------------------- - * dz_console_print () - * - * dz_console_print is registered for printk. - * The console must be locked when we get here. - * ------------------------------------------------------------------- - */ -static void dz_console_print (struct console *cons, - const char *str, - unsigned int count) -{ -#ifdef DEBUG_DZ - prom_printf((char *)str); -#endif - while (count--) { - if (*str == '\n') - dz_console_put_char('\r'); - dz_console_put_char(*str++); - } -} - -static struct tty_driver *dz_console_device(struct console *c, int *index) -{ - *index = c->index; - return serial_driver; -} - -static int __init dz_console_setup(struct console *co, char *options) -{ - int baud = 9600; - int bits = 8; - int parity = 'n'; - int cflag = CREAD | HUPCL | CLOCAL; - char *s; - unsigned short mask,tmp; - - 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 |= DZ_B1200; - break; - case 2400: - cflag |= DZ_B2400; - break; - case 4800: - cflag |= DZ_B4800; - break; - case 9600: - default: - cflag |= DZ_B9600; - break; - } - switch (bits) { - case 7: - cflag |= DZ_CS7; - break; - default: - case 8: - cflag |= DZ_CS8; - break; - } - switch (parity) { - case 'o': - case 'O': - cflag |= DZ_PARODD; - break; - case 'e': - case 'E': - cflag |= DZ_PARENB; - break; - } - co->cflag = cflag; - - /* TOFIX: force to console line */ - dz_console = &multi[CONSOLE_LINE]; - if ((mips_machtype == MACH_DS23100) || (mips_machtype == MACH_DS5100)) - dz_console->port = KN01_DZ11_BASE; - else - dz_console->port = KN02_DZ11_BASE; - dz_console->line = CONSOLE_LINE; - - dz_out(dz_console, DZ_CSR, DZ_CLR); - while ((tmp = dz_in(dz_console,DZ_CSR)) & DZ_CLR) - ; - - /* enable scanning */ - dz_out(dz_console, DZ_CSR, DZ_MSE); - - /* Set up flags... */ - dz_console->cflags = 0; - dz_console->cflags |= DZ_B9600; - dz_console->cflags |= DZ_CS8; - dz_console->cflags |= DZ_PARENB; - dz_out(dz_console, DZ_LPR, dz_console->cflags); - - mask = 1 << dz_console->line; - tmp = dz_in (dz_console, DZ_TCR); /* read the TX flag */ - if (!(tmp & mask)) { - tmp |= mask; /* set the TX flag */ - dz_out (dz_console, DZ_TCR, tmp); - } - - return 0; -} - -static struct console dz_sercons = { - .name = "ttyS", - .write = dz_console_print, - .device = dz_console_device, - .setup = dz_console_setup, - .flags = CON_CONSDEV | CON_PRINTBUFFER, - .index = CONSOLE_LINE, -}; - -void __init dz_serial_console_init(void) -{ - register_console(&dz_sercons); -} - -#endif /* ifdef CONFIG_SERIAL_CONSOLE */ - -MODULE_LICENSE("GPL"); diff --git a/drivers/char/dz.h b/drivers/char/dz.h deleted file mode 100644 index 989f927a4..000000000 --- a/drivers/char/dz.h +++ /dev/null @@ -1,230 +0,0 @@ -/* - * dz.h: Serial port driver for DECStations equiped - * with the DZ chipset. - * - * Copyright (C) 1998 Olivier A. D. Lebaillif - * - * Email: olivier.lebaillif@ifrsys.com - * - */ -#ifndef DZ_SERIAL_H -#define DZ_SERIAL_H - -/* - * Definitions for the Control and Status Received. - */ -#define DZ_TRDY 0x8000 /* Transmitter empty */ -#define DZ_TIE 0x4000 /* Transmitter Interrupt Enable */ -#define DZ_RDONE 0x0080 /* Receiver data ready */ -#define DZ_RIE 0x0040 /* Receive Interrupt Enable */ -#define DZ_MSE 0x0020 /* Master Scan Enable */ -#define DZ_CLR 0x0010 /* Master reset */ -#define DZ_MAINT 0x0008 /* Loop Back Mode */ - -/* - * Definitions for the Received buffer. - */ -#define DZ_RBUF_MASK 0x00FF /* Data Mask in the Receive Buffer */ -#define DZ_LINE_MASK 0x0300 /* Line Mask in the Receive Buffer */ -#define DZ_DVAL 0x8000 /* Valid Data indicator */ -#define DZ_OERR 0x4000 /* Overrun error indicator */ -#define DZ_FERR 0x2000 /* Frame error indicator */ -#define DZ_PERR 0x1000 /* Parity error indicator */ - -#define LINE(x) (x & DZ_LINE_MASK) >> 8 /* Get the line number from the input buffer */ -#define UCHAR(x) (unsigned char)(x & DZ_RBUF_MASK) - -/* - * Definitions for the Transmit Register. - */ -#define DZ_LINE_KEYBOARD 0x0001 -#define DZ_LINE_MOUSE 0x0002 -#define DZ_LINE_MODEM 0x0004 -#define DZ_LINE_PRINTER 0x0008 - -#define DZ_MODEM_DTR 0x0400 /* DTR for the modem line (2) */ - -/* - * Definitions for the Modem Status Register. - */ -#define DZ_MODEM_DSR 0x0200 /* DSR for the modem line (2) */ - -/* - * Definitions for the Transmit Data Register. - */ -#define DZ_BRK0 0x0100 /* Break assertion for line 0 */ -#define DZ_BRK1 0x0200 /* Break assertion for line 1 */ -#define DZ_BRK2 0x0400 /* Break assertion for line 2 */ -#define DZ_BRK3 0x0800 /* Break assertion for line 3 */ - -/* - * Definitions for the Line Parameter Register. - */ -#define DZ_KEYBOARD 0x0000 /* line 0 = keyboard */ -#define DZ_MOUSE 0x0001 /* line 1 = mouse */ -#define DZ_MODEM 0x0002 /* line 2 = modem */ -#define DZ_PRINTER 0x0003 /* line 3 = printer */ - -#define DZ_CSIZE 0x0018 /* Number of bits per byte (mask) */ -#define DZ_CS5 0x0000 /* 5 bits per byte */ -#define DZ_CS6 0x0008 /* 6 bits per byte */ -#define DZ_CS7 0x0010 /* 7 bits per byte */ -#define DZ_CS8 0x0018 /* 8 bits per byte */ - -#define DZ_CSTOPB 0x0020 /* 2 stop bits instead of one */ - -#define DZ_PARENB 0x0040 /* Parity enable */ -#define DZ_PARODD 0x0080 /* Odd parity instead of even */ - -#define DZ_CBAUD 0x0E00 /* Baud Rate (mask) */ -#define DZ_B50 0x0000 -#define DZ_B75 0x0100 -#define DZ_B110 0x0200 -#define DZ_B134 0x0300 -#define DZ_B150 0x0400 -#define DZ_B300 0x0500 -#define DZ_B600 0x0600 -#define DZ_B1200 0x0700 -#define DZ_B1800 0x0800 -#define DZ_B2000 0x0900 -#define DZ_B2400 0x0A00 -#define DZ_B3600 0x0B00 -#define DZ_B4800 0x0C00 -#define DZ_B7200 0x0D00 -#define DZ_B9600 0x0E00 - -#define DZ_CREAD 0x1000 /* Enable receiver */ -#define DZ_RXENAB 0x1000 /* enable receive char */ -/* - * Addresses for the DZ registers - */ -#define DZ_CSR 0x00 /* Control and Status Register */ -#define DZ_RBUF 0x08 /* Receive Buffer */ -#define DZ_LPR 0x08 /* Line Parameters Register */ -#define DZ_TCR 0x10 /* Transmitter Control Register */ -#define DZ_MSR 0x18 /* Modem Status Register */ -#define DZ_TDR 0x18 /* Transmit Data Register */ - - -#define DZ_NB_PORT 4 - -#define DZ_XMIT_SIZE 4096 /* buffer size */ -#define WAKEUP_CHARS DZ_XMIT_SIZE/4 - -#define DZ_EVENT_WRITE_WAKEUP 0 - -#ifndef MIN -#define MIN(a,b) ((a) < (b) ? (a) : (b)) - -#define DZ_INITIALIZED 0x80000000 /* Serial port was initialized */ -#define DZ_CALLOUT_ACTIVE 0x40000000 /* Call out device is active */ -#define DZ_NORMAL_ACTIVE 0x20000000 /* Normal device is active */ -#define DZ_BOOT_AUTOCONF 0x10000000 /* Autoconfigure port on bootup */ -#define DZ_CLOSING 0x08000000 /* Serial port is closing */ -#define DZ_CTS_FLOW 0x04000000 /* Do CTS flow control */ -#define DZ_CHECK_CD 0x02000000 /* i.e., CLOCAL */ - -#define DZ_CLOSING_WAIT_INF 0 -#define DZ_CLOSING_WAIT_NONE 65535 - -#define DZ_SPLIT_TERMIOS 0x0008 /* Separate termios for dialin/callout */ -#define DZ_SESSION_LOCKOUT 0x0100 /* Lock out cua opens based on session */ -#define DZ_PGRP_LOCKOUT 0x0200 /* Lock out cua opens based on pgrp */ - -struct dz_serial { - unsigned port; /* base address for the port */ - int type; - int flags; - int baud_base; - int blocked_open; - unsigned short close_delay; - unsigned short closing_wait; - unsigned short line; /* port/line number */ - unsigned short cflags; /* line configuration flag */ - unsigned short x_char; /* xon/xoff character */ - unsigned short read_status_mask; /* mask for read condition */ - unsigned short ignore_status_mask; /* mask for ignore condition */ - unsigned long event; /* mask used in BH */ - unsigned char *xmit_buf; /* Transmit buffer */ - int xmit_head; /* Position of the head */ - int xmit_tail; /* Position of the tail */ - int xmit_cnt; /* Count of the chars in the buffer */ - int count; /* indicates how many times it has been opened */ - int magic; - - struct async_icount icount; /* keep track of things ... */ - struct tty_struct *tty; /* tty associated */ - struct tq_struct tqueue; /* Queue for BH */ - struct tq_struct tqueue_hangup; - wait_queue_head_t open_wait; - wait_queue_head_t close_wait; - - unsigned char is_console; /* flag indicating a serial console */ - unsigned char is_initialized; -}; - -static struct dz_serial multi[DZ_NB_PORT]; /* Four serial lines in the DZ chip */ -static struct dz_serial *dz_console; - -/* - * 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; -static DECLARE_MUTEX(tmp_buf_sem); - -static char *dz_name = "DECstation DZ serial driver version "; -static char *dz_version = "1.02"; - -static inline unsigned short dz_in (struct dz_serial *, unsigned); -static inline void dz_out (struct dz_serial *, unsigned, unsigned short); - -static inline void dz_sched_event (struct dz_serial *, int); -static inline void receive_chars (struct dz_serial *); -static inline void transmit_chars (struct dz_serial *); -static inline void check_modem_status (struct dz_serial *); - -static void dz_stop (struct tty_struct *); -static void dz_start (struct tty_struct *); -static void dz_interrupt (int, void *, struct pt_regs *); -static void do_serial_bh (void); -static void do_softint (void *); -static void do_serial_hangup (void *); -static void change_speed (struct dz_serial *); -static void dz_flush_chars (struct tty_struct *); -static void dz_console_print (struct console *, const char *, unsigned int); -static void dz_flush_buffer (struct tty_struct *); -static void dz_throttle (struct tty_struct *); -static void dz_unthrottle (struct tty_struct *); -static void dz_send_xchar (struct tty_struct *, char); -static void shutdown (struct dz_serial *); -static void send_break (struct dz_serial *, int); -static void dz_set_termios (struct tty_struct *, struct termios *); -static void dz_close (struct tty_struct *, struct file *); -static void dz_hangup (struct tty_struct *); -static void show_serial_version (void); - -static int dz_write (struct tty_struct *, int, const unsigned char *, int); -static int dz_write_room (struct tty_struct *); -static int dz_chars_in_buffer (struct tty_struct *); -static int startup (struct dz_serial *); -static int get_serial_info (struct dz_serial *, struct serial_struct *); -static int set_serial_info (struct dz_serial *, struct serial_struct *); -static int get_lsr_info (struct dz_serial *, unsigned int *); -static int dz_ioctl (struct tty_struct *, struct file *, unsigned int, unsigned long); -static int block_til_ready (struct tty_struct *, struct file *, struct dz_serial *); -static int dz_open (struct tty_struct *, struct file *); - -#ifdef MODULE -int init_module (void) -void cleanup_module (void) -#endif - -#endif - -#endif /* DZ_SERIAL_H */ diff --git a/drivers/char/sh-sci.c b/drivers/char/sh-sci.c deleted file mode 100644 index d3894a6f9..000000000 --- a/drivers/char/sh-sci.c +++ /dev/null @@ -1,1646 +0,0 @@ -/* $Id: sh-sci.c,v 1.16 2004/02/10 17:04:17 lethal Exp $ - * - * linux/drivers/char/sh-sci.c - * - * SuperH on-chip serial module support. (SCI with no FIFO / with FIFO) - * Copyright (C) 1999, 2000 Niibe Yutaka - * Copyright (C) 2000 Sugioka Toshinobu - * Modified to support multiple serial ports. Stuart Menefy (May 2000). - * Modified to support SH7760 SCIF. Paul Mundt (Oct 2003). - * Modified to support H8/300 Series. Yoshinori Sato (Feb 2004). - * - * TTY code is based on sx.c (Specialix SX driver) by: - * - * (C) 1998 R.E.Wolff@BitWizard.nl - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#if defined(CONFIG_SERIAL_CONSOLE) || defined(CONFIG_SH_KGDB_CONSOLE) -#include -#endif -#ifdef CONFIG_CPU_FREQ -#include -#include -#endif - -#include -#include -#include -#include -#include - -#include - -#ifdef CONFIG_SH_STANDARD_BIOS -#include -#endif - -#include "sh-sci.h" - -#ifdef CONFIG_SH_KGDB -#include - -int kgdb_sci_setup(void); -static int kgdb_get_char(struct sci_port *port); -static void kgdb_put_char(struct sci_port *port, char c); -static void kgdb_handle_error(struct sci_port *port); -static struct sci_port *kgdb_sci_port; - -#ifdef CONFIG_SH_KGDB_CONSOLE -static struct console kgdbcons; -void __init kgdb_console_init(void); -#endif /* CONFIG_SH_KGDB_CONSOLE */ - -#endif /* CONFIG_SH_KGDB */ - -#ifdef CONFIG_SERIAL_CONSOLE -static struct console sercons; -static struct sci_port* sercons_port=0; -static int sercons_baud; -#ifdef CONFIG_MAGIC_SYSRQ -#include -static int break_pressed; -#endif /* CONFIG_MAGIC_SYSRQ */ -#endif /* CONFIG_SERIAL_CONSOLE */ - -/* Function prototypes */ -static void sci_init_pins_sci(struct sci_port* port, unsigned int cflag); -#ifndef SCI_ONLY -static void sci_init_pins_scif(struct sci_port* port, unsigned int cflag); -#if defined(CONFIG_CPU_SH3) -static void sci_init_pins_irda(struct sci_port* port, unsigned int cflag); -#endif -#endif -static void sci_disable_tx_interrupts(void *ptr); -static void sci_enable_tx_interrupts(void *ptr); -static void sci_disable_rx_interrupts(void *ptr); -static void sci_enable_rx_interrupts(void *ptr); -static int sci_get_CD(void *ptr); -static void sci_shutdown_port(void *ptr); -static int sci_set_real_termios(void *ptr); -static void sci_hungup(void *ptr); -static void sci_close(void *ptr); -static int sci_chars_in_buffer(void *ptr); -static int sci_request_irq(struct sci_port *port); -static void sci_free_irq(struct sci_port *port); -static int sci_init_drivers(void); - -static struct tty_driver *sci_driver; - -static struct sci_port sci_ports[SCI_NPORTS] = SCI_INIT; - -static int sci_debug = 0; - -#ifdef MODULE -MODULE_PARM(sci_debug, "i"); -#endif - -#define dprintk(x...) do { if (sci_debug) printk(x); } while(0) - -#ifdef CONFIG_SERIAL_CONSOLE -static void put_char(struct sci_port *port, char c) -{ - unsigned long flags; - unsigned short status; - - local_irq_save(flags); - - do - status = sci_in(port, SCxSR); - while (!(status & SCxSR_TDxE(port))); - - sci_out(port, SCxTDR, c); - sci_in(port, SCxSR); /* Dummy read */ - sci_out(port, SCxSR, SCxSR_TDxE_CLEAR(port)); - - local_irq_restore(flags); -} -#endif - -#if defined(CONFIG_SH_STANDARD_BIOS) || defined(CONFIG_SH_KGDB) - -static void handle_error(struct sci_port *port) -{ /* Clear error flags */ - sci_out(port, SCxSR, SCxSR_ERROR_CLEAR(port)); -} - -static int get_char(struct sci_port *port) -{ - unsigned long flags; - unsigned short status; - int c; - - local_irq_save(flags); - do { - status = sci_in(port, SCxSR); - if (status & SCxSR_ERRORS(port)) { - handle_error(port); - continue; - } - } while (!(status & SCxSR_RDxF(port))); - c = sci_in(port, SCxRDR); - sci_in(port, SCxSR); /* Dummy read */ - sci_out(port, SCxSR, SCxSR_RDxF_CLEAR(port)); - local_irq_restore(flags); - - return c; -} - -/* Taken from sh-stub.c of GDB 4.18 */ -static const char hexchars[] = "0123456789abcdef"; - -static __inline__ char highhex(int x) -{ - return hexchars[(x >> 4) & 0xf]; -} - -static __inline__ char lowhex(int x) -{ - return hexchars[x & 0xf]; -} - -#endif /* CONFIG_SH_STANDARD_BIOS || CONFIG_SH_KGDB */ - -/* - * Send the packet in buffer. The host gets one chance to read it. - * This routine does not wait for a positive acknowledge. - */ - -#ifdef CONFIG_SERIAL_CONSOLE -static void put_string(struct sci_port *port, const char *buffer, int count) -{ - int i; - const unsigned char *p = buffer; - -#if defined(CONFIG_SH_STANDARD_BIOS) || defined(CONFIG_SH_KGDB) - int checksum; - int usegdb=0; - -#ifdef CONFIG_SH_STANDARD_BIOS - /* This call only does a trap the first time it is - * called, and so is safe to do here unconditionally - */ - usegdb |= sh_bios_in_gdb_mode(); -#endif -#ifdef CONFIG_SH_KGDB - usegdb |= (kgdb_in_gdb_mode && (port == kgdb_sci_port)); -#endif - - if (usegdb) { - /* $#. */ - do { - unsigned char c; - put_char(port, '$'); - put_char(port, 'O'); /* 'O'utput to console */ - checksum = 'O'; - - for (i=0; ibase - SMR0) >> 3; - /* set DDR regs */ - H8300_GPIO_DDR(h8300_sci_pins[ch].port,h8300_sci_pins[ch].rx,H8300_GPIO_INPUT); - H8300_GPIO_DDR(h8300_sci_pins[ch].port,h8300_sci_pins[ch].tx,H8300_GPIO_OUTPUT); - /* tx mark output*/ - H8300_SCI_DR(ch) |= h8300_sci_pins[ch].tx; -} - -#if defined(__H8300S__) -enum {sci_disable,sci_enable}; - -static void h8300_sci_enable(struct sci_port* port, unsigned int ctrl) -{ - volatile unsigned char *mstpcrl=(volatile unsigned char *)MSTPCRL; - int ch = (port->base - SMR0) >> 3; - unsigned char mask = 1 << (ch+1); - if (ctrl == sci_disable) - *mstpcrl |= mask; - else - *mstpcrl &= ~mask; -} -#endif -#endif - -static void sci_setsignals(struct sci_port *port, int dtr, int rts) -{ - /* This routine is used for seting signals of: DTR, DCD, CTS/RTS */ - /* We use SCIF's hardware for CTS/RTS, so don't need any for that. */ - /* If you have signals for DTR and DCD, please implement here. */ - ; -} - -static int sci_getsignals(struct sci_port *port) -{ - /* This routine is used for geting signals of: DTR, DCD, DSR, RI, - and CTS/RTS */ - - return TIOCM_DTR|TIOCM_RTS|TIOCM_DSR; -/* - (((o_stat & OP_DTR)?TIOCM_DTR:0) | - ((o_stat & OP_RTS)?TIOCM_RTS:0) | - ((i_stat & IP_CTS)?TIOCM_CTS:0) | - ((i_stat & IP_DCD)?TIOCM_CAR:0) | - ((i_stat & IP_DSR)?TIOCM_DSR:0) | - ((i_stat & IP_RI) ?TIOCM_RNG:0) -*/ -} - -static void sci_set_baud(struct sci_port *port, int baud) -{ - int t; - - switch (baud) { - case 0: - t = -1; - break; - case 2400: - t = BPS_2400; - break; - case 4800: - t = BPS_4800; - break; - case 9600: - t = BPS_9600; - break; - case 19200: - t = BPS_19200; - break; - case 38400: - t = BPS_38400; - break; - case 57600: - t = BPS_57600; - break; - default: - printk(KERN_INFO "sci: unsupported baud rate: %d, using 115200 instead.\n", baud); - case 115200: - t = BPS_115200; - break; - } - - if (t > 0) { - sci_setsignals (port, 1, -1); - if(t >= 256) { - sci_out(port, SCSMR, (sci_in(port, SCSMR) & ~3) | 1); - t >>= 2; - } else { - sci_out(port, SCSMR, sci_in(port, SCSMR) & ~3); - } - sci_out(port, SCBRR, t); - udelay((1000000+(baud-1)) / baud); /* Wait one bit interval */ - } else { - sci_setsignals (port, 0, -1); - } -} - -static void sci_set_termios_cflag(struct sci_port *port, int cflag, int baud) -{ - unsigned int status; - unsigned int smr_val; - - do - status = sci_in(port, SCxSR); - while (!(status & SCxSR_TEND(port))); - - sci_out(port, SCSCR, 0x00); /* TE=0, RE=0, CKE1=0 */ - -#if !defined(SCI_ONLY) - if (port->type == PORT_SCIF) { - sci_out(port, SCFCR, SCFCR_RFRST | SCFCR_TFRST); - } -#endif - - smr_val = sci_in(port, SCSMR) & 3; - if ((cflag & CSIZE) == CS7) - smr_val |= 0x40; - if (cflag & PARENB) - smr_val |= 0x20; - if (cflag & PARODD) - smr_val |= 0x30; - if (cflag & CSTOPB) - smr_val |= 0x08; - sci_out(port, SCSMR, smr_val); - sci_set_baud(port, baud); - - port->init_pins(port, cflag); - sci_out(port, SCSCR, SCSCR_INIT(port)); -} - -static int sci_set_real_termios(void *ptr) -{ - struct sci_port *port = ptr; - - if (port->old_cflag != port->gs.tty->termios->c_cflag) { - port->old_cflag = port->gs.tty->termios->c_cflag; - sci_set_termios_cflag(port, port->old_cflag, port->gs.baud); - sci_enable_rx_interrupts(port); - } - - return 0; -} - -/* ********************************************************************** * - * the interrupt related routines * - * ********************************************************************** */ - -/* - * This routine is used by the interrupt handler to schedule - * processing in the software interrupt portion of the driver. - */ -static inline void sci_sched_event(struct sci_port *port, int event) -{ - port->event |= 1 << event; - schedule_work(&port->tqueue); -} - -static void sci_transmit_chars(struct sci_port *port) -{ - int count, i; - int txroom; - unsigned long flags; - unsigned short status; - unsigned short ctrl; - unsigned char c; - - status = sci_in(port, SCxSR); - if (!(status & SCxSR_TDxE(port))) { - local_irq_save(flags); - ctrl = sci_in(port, SCSCR); - if (port->gs.xmit_cnt == 0) { - ctrl &= ~SCI_CTRL_FLAGS_TIE; - port->gs.flags &= ~GS_TX_INTEN; - } else - ctrl |= SCI_CTRL_FLAGS_TIE; - sci_out(port, SCSCR, ctrl); - local_irq_restore(flags); - return; - } - - while (1) { - count = port->gs.xmit_cnt; -#if !defined(SCI_ONLY) - if (port->type == PORT_SCIF) { - txroom = 16 - (sci_in(port, SCFDR)>>8); - } else { - txroom = (sci_in(port, SCxSR) & SCI_TDRE)?1:0; - } -#else - txroom = (sci_in(port, SCxSR) & SCI_TDRE)?1:0; -#endif - if (count > txroom) - count = txroom; - - /* Don't copy past the end of the source buffer */ - if (count > SERIAL_XMIT_SIZE - port->gs.xmit_tail) - count = SERIAL_XMIT_SIZE - port->gs.xmit_tail; - - /* If for one reason or another, we can't copy more data, we're done! */ - if (count == 0) - break; - - for (i=0; igs.xmit_buf[port->gs.xmit_tail + i]; - sci_out(port, SCxTDR, c); - } - sci_out(port, SCxSR, SCxSR_TDxE_CLEAR(port)); - - port->icount.tx += count; - - /* Update the kernel buffer end */ - port->gs.xmit_tail = (port->gs.xmit_tail + count) & (SERIAL_XMIT_SIZE-1); - - /* This one last. (this is essential) - It would allow others to start putting more data into the buffer! */ - port->gs.xmit_cnt -= count; - } - - if (port->gs.xmit_cnt <= port->gs.wakeup_chars) - sci_sched_event(port, SCI_EVENT_WRITE_WAKEUP); - - local_irq_save(flags); - ctrl = sci_in(port, SCSCR); - if (port->gs.xmit_cnt == 0) { - ctrl &= ~SCI_CTRL_FLAGS_TIE; - port->gs.flags &= ~GS_TX_INTEN; - } else { -#if !defined(SCI_ONLY) - if (port->type == PORT_SCIF) { - sci_in(port, SCxSR); /* Dummy read */ - sci_out(port, SCxSR, SCxSR_TDxE_CLEAR(port)); - } -#endif - ctrl |= SCI_CTRL_FLAGS_TIE; - } - sci_out(port, SCSCR, ctrl); - local_irq_restore(flags); -} - -/* On SH3, SCIF may read end-of-break as a space->mark char */ -#define STEPFN(c) ({int __c=(c); (((__c-1)|(__c)) == -1); }) - -static inline void sci_receive_chars(struct sci_port *port, - struct pt_regs *regs) -{ - int i, count; - struct tty_struct *tty; - int copied=0; - unsigned short status; - - status = sci_in(port, SCxSR); - if (!(status & SCxSR_RDxF(port))) - return; - - tty = port->gs.tty; - while (1) { -#if !defined(SCI_ONLY) - if (port->type == PORT_SCIF) { - count = sci_in(port, SCFDR)&0x001f; - } else { - count = (sci_in(port, SCxSR)&SCxSR_RDxF(port))?1:0; - } -#else - count = (sci_in(port, SCxSR)&SCxSR_RDxF(port))?1:0; -#endif - - /* Don't copy more bytes than there is room for in the buffer */ - if (tty->flip.count + count > TTY_FLIPBUF_SIZE) - count = TTY_FLIPBUF_SIZE - tty->flip.count; - - /* If for any reason we can't copy more data, we're done! */ - if (count == 0) - break; - - if (port->type == PORT_SCI) { - tty->flip.char_buf_ptr[0] = sci_in(port, SCxRDR); - tty->flip.flag_buf_ptr[0] = TTY_NORMAL; - } else { - for (i=0; ibreak_flag) { - if ((c == 0) && - (status & SCxSR_FER(port))) { - count--; i--; - continue; - } - /* Nonzero => end-of-break */ - dprintk("scif: debounce<%02x>\n", c); - port->break_flag = 0; - if (STEPFN(c)) { - count--; i--; - continue; - } - } -#endif /* __SH3__ */ -#if defined(CONFIG_SERIAL_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) - if (break_pressed && (port == sercons_port)) { - if (c != 0 && - time_before(jiffies, - break_pressed + HZ*5)) { - handle_sysrq(c, regs, NULL); - break_pressed = 0; - count--; i--; - continue; - } else if (c != 0) { - break_pressed = 0; - } - } -#endif /* CONFIG_SERIAL_CONSOLE && CONFIG_MAGIC_SYSRQ */ - - /* Store data and status */ - tty->flip.char_buf_ptr[i] = c; - if (status&SCxSR_FER(port)) { - tty->flip.flag_buf_ptr[i] = TTY_FRAME; - dprintk("sci: frame error\n"); - } else if (status&SCxSR_PER(port)) { - tty->flip.flag_buf_ptr[i] = TTY_PARITY; - dprintk("sci: parity error\n"); - } else { - tty->flip.flag_buf_ptr[i] = TTY_NORMAL; - } - } - } - - sci_in(port, SCxSR); /* dummy read */ - sci_out(port, SCxSR, SCxSR_RDxF_CLEAR(port)); - - /* Update the kernel buffer end */ - tty->flip.count += count; - tty->flip.char_buf_ptr += count; - tty->flip.flag_buf_ptr += count; - - copied += count; - port->icount.rx += count; - } - - if (copied) - /* Tell the rest of the system the news. New characters! */ - tty_flip_buffer_push(tty); - else { - sci_in(port, SCxSR); /* dummy read */ - sci_out(port, SCxSR, SCxSR_RDxF_CLEAR(port)); - } -} - -static inline int sci_handle_errors(struct sci_port *port) -{ - int copied = 0; - unsigned short status = sci_in(port, SCxSR); - struct tty_struct *tty = port->gs.tty; - - if (status&SCxSR_ORER(port) && tty->flip.countflip.flag_buf_ptr++ = TTY_OVERRUN; - dprintk("sci: overrun error\n"); - } - - if (status&SCxSR_FER(port) && tty->flip.countflip.flag_buf_ptr++ = TTY_BREAK; - dprintk("sci: BREAK detected\n"); - } - else { - /* frame error */ - copied++; - *tty->flip.flag_buf_ptr++ = TTY_FRAME; - dprintk("sci: frame error\n"); - } - } - - if (status&SCxSR_PER(port) && tty->flip.countflip.flag_buf_ptr++ = TTY_PARITY; - dprintk("sci: parity error\n"); - } - - if (copied) { - tty->flip.count += copied; - tty_flip_buffer_push(tty); - } - - return copied; -} - -static inline int sci_handle_breaks(struct sci_port *port) -{ - int copied = 0; - unsigned short status = sci_in(port, SCxSR); - struct tty_struct *tty = port->gs.tty; - - if (status&SCxSR_BRK(port) && tty->flip.countbreak_flag) - goto break_continue; - port->break_flag = 1; -#endif -#if defined(CONFIG_SERIAL_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) - if (port == sercons_port) { - if (break_pressed == 0) { - break_pressed = jiffies; - dprintk("sci: implied sysrq\n"); - goto break_continue; - } - /* Double break implies a real break */ - break_pressed = 0; - } -#endif - /* Notify of BREAK */ - copied++; - *tty->flip.flag_buf_ptr++ = TTY_BREAK; - dprintk("sci: BREAK detected\n"); - } - break_continue: - -#if defined(CONFIG_CPU_SUBTYPE_SH7750) || defined(CONFIG_CPU_SUBTYPE_ST40STB1) || \ - defined(CONFIG_CPU_SUBTYPE_SH7760) - /* XXX: Handle SCIF overrun error */ - if (port->type == PORT_SCIF && (sci_in(port, SCLSR) & SCIF_ORER) != 0) { - sci_out(port, SCLSR, 0); - if(tty->flip.countflip.flag_buf_ptr++ = TTY_OVERRUN; - dprintk("sci: overrun error\n"); - } - } -#endif - - if (copied) { - tty->flip.count += copied; - tty_flip_buffer_push(tty); - } - - return copied; -} - -static irqreturn_t sci_rx_interrupt(int irq, void *ptr, struct pt_regs *regs) -{ - struct sci_port *port = ptr; - - if (port->gs.flags & GS_ACTIVE) - if (!(port->gs.flags & SCI_RX_THROTTLE)) { - sci_receive_chars(port, regs); - return IRQ_HANDLED; - - } - sci_disable_rx_interrupts(port); - - return IRQ_HANDLED; -} - -static irqreturn_t sci_tx_interrupt(int irq, void *ptr, struct pt_regs *regs) -{ - struct sci_port *port = ptr; - - if (port->gs.flags & GS_ACTIVE) - sci_transmit_chars(port); - else { - sci_disable_tx_interrupts(port); - } - - return IRQ_HANDLED; -} - -static irqreturn_t sci_er_interrupt(int irq, void *ptr, struct pt_regs *regs) -{ - struct sci_port *port = ptr; - - /* Handle errors */ - if (port->type == PORT_SCI) { - if(sci_handle_errors(port)) { - /* discard character in rx buffer */ - sci_in(port, SCxSR); - sci_out(port, SCxSR, SCxSR_RDxF_CLEAR(port)); - } - } - else - sci_rx_interrupt(irq, ptr, regs); - - sci_out(port, SCxSR, SCxSR_ERROR_CLEAR(port)); - - /* Kick the transmission */ - sci_tx_interrupt(irq, ptr, regs); - - return IRQ_HANDLED; -} - -#if !defined(SCI_ONLY) -static irqreturn_t sci_br_interrupt(int irq, void *ptr, struct pt_regs *regs) -{ - struct sci_port *port = ptr; - - /* Handle BREAKs */ - sci_handle_breaks(port); - sci_out(port, SCxSR, SCxSR_BREAK_CLEAR(port)); - - return IRQ_HANDLED; -} -#endif - -static void do_softint(void *private_) -{ - struct sci_port *port = (struct sci_port *) private_; - struct tty_struct *tty; - - tty = port->gs.tty; - if (!tty) - return; - - if (test_and_clear_bit(SCI_EVENT_WRITE_WAKEUP, &port->event)) { - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); - wake_up_interruptible(&tty->write_wait); - } -} - -/* ********************************************************************** * - * Here are the routines that actually * - * interface with the generic_serial driver * - * ********************************************************************** */ - -static void sci_disable_tx_interrupts(void *ptr) -{ - struct sci_port *port = ptr; - unsigned long flags; - unsigned short ctrl; - - /* Clear TIE (Transmit Interrupt Enable) bit in SCSCR */ - local_irq_save(flags); - ctrl = sci_in(port, SCSCR); - ctrl &= ~SCI_CTRL_FLAGS_TIE; - sci_out(port, SCSCR, ctrl); - local_irq_restore(flags); -} - -static void sci_enable_tx_interrupts(void *ptr) -{ - struct sci_port *port = ptr; - - disable_irq(port->irqs[SCIx_TXI_IRQ]); - sci_transmit_chars(port); - enable_irq(port->irqs[SCIx_TXI_IRQ]); -} - -static void sci_disable_rx_interrupts(void * ptr) -{ - struct sci_port *port = ptr; - unsigned long flags; - unsigned short ctrl; - - /* Clear RIE (Receive Interrupt Enable) bit in SCSCR */ - local_irq_save(flags); - ctrl = sci_in(port, SCSCR); - ctrl &= ~SCI_CTRL_FLAGS_RIE; - sci_out(port, SCSCR, ctrl); - local_irq_restore(flags); -} - -static void sci_enable_rx_interrupts(void * ptr) -{ - struct sci_port *port = ptr; - unsigned long flags; - unsigned short ctrl; - - /* Set RIE (Receive Interrupt Enable) bit in SCSCR */ - local_irq_save(flags); - ctrl = sci_in(port, SCSCR); - ctrl |= SCI_CTRL_FLAGS_RIE; - sci_out(port, SCSCR, ctrl); - local_irq_restore(flags); -} - -static int sci_get_CD(void * ptr) -{ - /* If you have signal for CD (Carrier Detect), please change here. */ - return 1; -} - -static int sci_chars_in_buffer(void * ptr) -{ - struct sci_port *port = ptr; - -#if !defined(SCI_ONLY) - if (port->type == PORT_SCIF) { - return (sci_in(port, SCFDR) >> 8) + ((sci_in(port, SCxSR) & SCxSR_TEND(port))? 0: 1); - } else { - return (sci_in(port, SCxSR) & SCxSR_TEND(port))? 0: 1; - } -#else - return (sci_in(port, SCxSR) & SCxSR_TEND(port))? 0: 1; -#endif -} - -static void sci_shutdown_port(void * ptr) -{ - struct sci_port *port = ptr; - - port->gs.flags &= ~ GS_ACTIVE; - if (port->gs.tty && port->gs.tty->termios->c_cflag & HUPCL) - sci_setsignals(port, 0, 0); - sci_free_irq(port); -#if defined(__H8300S__) - h8300_sci_enable(port,sci_disable); -#endif -} - -/* ********************************************************************** * - * Here are the routines that actually * - * interface with the rest of the system * - * ********************************************************************** */ - -static int sci_open(struct tty_struct * tty, struct file * filp) -{ - struct sci_port *port; - int retval, line; - - line = tty->index; - - if ((line < 0) || (line >= SCI_NPORTS)) - return -ENODEV; - - port = &sci_ports[line]; - - tty->driver_data = port; - port->gs.tty = tty; - port->gs.count++; - - port->event = 0; - INIT_WORK(&port->tqueue, do_softint, port); - -#if defined(__H8300S__) - h8300_sci_enable(port,sci_enable); -#endif - - /* - * Start up serial port - */ - retval = gs_init_port(&port->gs); - if (retval) { - goto failed_1; - } - - port->gs.flags |= GS_ACTIVE; - sci_setsignals(port, 1,1); - - if (port->gs.count == 1) { - retval = sci_request_irq(port); - } - - retval = gs_block_til_ready(port, filp); - - if (retval) { - goto failed_3; - } - -#ifdef CONFIG_SERIAL_CONSOLE - if (sercons.cflag && sercons.index == line) { - tty->termios->c_cflag = sercons.cflag; - port->gs.baud = sercons_baud; - sercons.cflag = 0; - sci_set_real_termios(port); - } -#endif - -#ifdef CONFIG_SH_KGDB_CONSOLE - if (kgdbcons.cflag && kgdbcons.index == line) { - tty->termios->c_cflag = kgdbcons.cflag; - port->gs.baud = kgdb_baud; - sercons.cflag = 0; - sci_set_real_termios(port); - } -#endif - - sci_enable_rx_interrupts(port); - - return 0; - -failed_3: - sci_free_irq(port); -failed_1: - port->gs.count--; - return retval; -} - -static void sci_hungup(void *ptr) -{ - return; -} - -static void sci_close(void *ptr) -{ - return; -} - -static int sci_tiocmget(struct tty_struct *tty, struct file *file) -{ - struct sci_port *port = tty->driver_data; - return sci_getsignals(port); -} - -static int sci_tiocmset(struct tty_struct *tty, struct file *file, - unsigned int set, unsigned int clear) -{ - struct sci_port *port = tty->driver_data; - int rts = -1, dtr = -1; - - if (set & TIOCM_RTS) - rts = 1; - if (set & TIOCM_DTR) - dtr = 1; - if (clear & TIOCM_RTS) - rts = 0; - if (clear & TIOCM_DTR) - dtr = 0; - - sci_setsignals(port, dtr, rts); - return 0; -} - -static int sci_ioctl(struct tty_struct * tty, struct file * filp, - unsigned int cmd, unsigned long arg) -{ - int rc; - struct sci_port *port = tty->driver_data; - int ival; - - rc = 0; - switch (cmd) { - case TIOCGSOFTCAR: - rc = put_user(((tty->termios->c_cflag & CLOCAL) ? 1 : 0), - (unsigned int __user *) arg); - break; - case TIOCSSOFTCAR: - if ((rc = get_user(ival, (unsigned int __user *) arg)) == 0) - tty->termios->c_cflag = - (tty->termios->c_cflag & ~CLOCAL) | - (ival ? CLOCAL : 0); - break; - case TIOCGSERIAL: - if ((rc = verify_area(VERIFY_WRITE, (void __user *) arg, - sizeof(struct serial_struct))) == 0) - rc = gs_getserial(&port->gs, (struct serial_struct *) arg); - break; - case TIOCSSERIAL: - if ((rc = verify_area(VERIFY_READ, (void __user *) arg, - sizeof(struct serial_struct))) == 0) - rc = gs_setserial(&port->gs, - (struct serial_struct *) arg); - break; - default: - rc = -ENOIOCTLCMD; - break; - } - - return rc; -} - -static void sci_throttle(struct tty_struct * tty) -{ - struct sci_port *port = (struct sci_port *)tty->driver_data; - - /* If the port is using any type of input flow - * control then throttle the port. - */ - if ((tty->termios->c_cflag & CRTSCTS) || (I_IXOFF(tty)) ) - port->gs.flags |= SCI_RX_THROTTLE; -} - -static void sci_unthrottle(struct tty_struct * tty) -{ - struct sci_port *port = (struct sci_port *)tty->driver_data; - - /* Always unthrottle even if flow control is not enabled on - * this port in case we disabled flow control while the port - * was throttled - */ - port->gs.flags &= ~SCI_RX_THROTTLE; - sci_enable_rx_interrupts(port); - return; -} - -#ifdef CONFIG_PROC_FS -static int sci_read_proc(char *page, char **start, off_t off, int count, - int *eof, void *data) -{ - int i; - struct sci_port *port; - int len = 0; - - len += sprintf(page, "sciinfo:0.1\n"); - for (i = 0; i < SCI_NPORTS && len < 4000; i++) { - port = &sci_ports[i]; - len += sprintf(page+len, "%d: uart:%s address: %08x", i, - (port->type == PORT_SCI) ? "SCI" : "SCIF", - port->base); - len += sprintf(page+len, " baud:%d", port->gs.baud); - len += sprintf(page+len, " tx:%d rx:%d", - port->icount.tx, port->icount.rx); - - if (port->icount.frame) - len += sprintf(page+len, " fe:%d", port->icount.frame); - if (port->icount.parity) - len += sprintf(page+len, " pe:%d", port->icount.parity); - if (port->icount.brk) - len += sprintf(page+len, " brk:%d", port->icount.brk); - if (port->icount.overrun) - len += sprintf(page+len, " oe:%d", port->icount.overrun); - len += sprintf(page+len, "\n"); - } - return len; -} -#endif - -#ifdef CONFIG_CPU_FREQ -/* - * Here we define a transistion notifier so that we can update all of our - * ports' baud rate when the peripheral clock changes. - */ - -static int sci_notifier(struct notifier_block *self, unsigned long phase, void *p) -{ - struct cpufreq_freqs *freqs = p; - int i; - - if (phase == CPUFREQ_POSTCHANGE) { - for (i = 0; i < SCI_NPORTS; i++) { - /* - * This will force a baud rate change in hardware. - */ - if (sci_ports[i].gs.tty != NULL) { - sci_set_baud(&sci_ports[i], sci_ports[i].gs.baud); - } - } - printk("%s: got a postchange notification for cpu %d (old %d, new %d)\n", - __FUNCTION__, freqs->cpu, freqs->old, freqs->new); - } - - return NOTIFY_OK; -} - -static struct notifier_block sci_nb = { &sci_notifier, NULL, 0 }; -#endif /* CONFIG_CPU_FREQ */ - -static struct tty_operations sci_ops = { - .open = sci_open, - .close = gs_close, - .write = gs_write, - .put_char = gs_put_char, - .flush_chars = gs_flush_chars, - .write_room = gs_write_room, - .chars_in_buffer = gs_chars_in_buffer, - .flush_buffer = gs_flush_buffer, - .ioctl = sci_ioctl, - .throttle = sci_throttle, - .unthrottle = sci_unthrottle, - .set_termios = gs_set_termios, - .stop = gs_stop, - .start = gs_start, - .hangup = gs_hangup, -#ifdef CONFIG_PROC_FS - .read_proc = sci_read_proc, -#endif - .tiocmget = sci_tiocmget, - .tiocmset = sci_tiocmset, -}; - -/* ********************************************************************** * - * Here are the initialization routines. * - * ********************************************************************** */ - -static int sci_init_drivers(void) -{ - int error; - struct sci_port *port; - sci_driver = alloc_tty_driver(SCI_NPORTS); - if (!sci_driver) - return -ENOMEM; - - sci_driver->owner = THIS_MODULE; - sci_driver->driver_name = "sci"; - sci_driver->name = "ttySC"; - sci_driver->devfs_name = "ttsc/"; - sci_driver->major = SCI_MAJOR; - sci_driver->minor_start = SCI_MINOR_START; - sci_driver->type = TTY_DRIVER_TYPE_SERIAL; - sci_driver->subtype = SERIAL_TYPE_NORMAL; - sci_driver->init_termios = tty_std_termios; - sci_driver->init_termios.c_cflag = - B9600 | CS8 | CREAD | HUPCL | CLOCAL | CRTSCTS; - sci_driver->flags = TTY_DRIVER_REAL_RAW; - tty_set_operations(sci_driver, &sci_ops); - if ((error = tty_register_driver(sci_driver))) { - printk(KERN_ERR "sci: Couldn't register SCI driver, error = %d\n", - error); - put_tty_driver(sci_driver); - return 1; - } - - for (port = &sci_ports[0]; port < &sci_ports[SCI_NPORTS]; port++) { - port->gs.magic = SCI_MAGIC; - port->gs.close_delay = HZ/2; - port->gs.closing_wait = 30 * HZ; - port->gs.rd = &sci_real_driver; - init_waitqueue_head(&port->gs.open_wait); - init_waitqueue_head(&port->gs.close_wait); - port->old_cflag = 0; - port->icount.cts = port->icount.dsr = - port->icount.rng = port->icount.dcd = 0; - port->icount.rx = port->icount.tx = 0; - port->icount.frame = port->icount.parity = 0; - port->icount.overrun = port->icount.brk = 0; - } - -#ifdef CONFIG_CPU_FREQ - /* Setup transition notifier */ - if (cpufreq_register_notifier(&sci_nb, CPUFREQ_TRANSITION_NOTIFIER) < 0) { - printk(KERN_ERR "sci: Unable to register CPU frequency notifier\n"); - return 1; - } - printk("sci: CPU frequency notifier registered\n"); -#endif - return 0; -} - -static int sci_request_irq(struct sci_port *port) -{ - int i; -#if !defined(SCI_ONLY) - irqreturn_t (*handlers[4])(int irq, void *p, struct pt_regs *regs) = { - sci_er_interrupt, sci_rx_interrupt, sci_tx_interrupt, - sci_br_interrupt, - }; -#else - void (*handlers[3])(int irq, void *ptr, struct pt_regs *regs) = { - sci_er_interrupt, sci_rx_interrupt, sci_tx_interrupt, - }; -#endif - for (i=0; i<(sizeof(handlers)/sizeof(handlers[0])); i++) { - if (!port->irqs[i]) continue; - if (request_irq(port->irqs[i], handlers[i], SA_INTERRUPT, - "sci", port)) { - printk(KERN_ERR "sci: Cannot allocate irq.\n"); - return -ENODEV; - } - } - return 0; -} - -static void sci_free_irq(struct sci_port *port) -{ - int i; - - for (i=0; i<4; i++) { - if (!port->irqs[i]) continue; - free_irq(port->irqs[i], port); - } -} - -static char banner[] __initdata = - KERN_INFO "SuperH SCI(F) driver initialized\n"; - -int __init sci_init(void) -{ - struct sci_port *port; - int j; - - printk("%s", banner); - - for (j=0; jbase, - (port->type == PORT_SCI) ? "SCI" : "SCIF"); - } - - sci_init_drivers(); - -#ifdef CONFIG_SH_STANDARD_BIOS - sh_bios_gdb_detach(); -#endif - return 0; /* Return -EIO when not detected */ -} - -module_init(sci_init); - -#ifdef MODULE -#undef func_enter -#undef func_exit - -void cleanup_module(void) -{ - tty_unregister_driver(sci_driver); - put_tty_driver(sci_driver); -} - -#include "generic_serial.c" -#endif - -#ifdef CONFIG_SERIAL_CONSOLE -/* - * Print a string to the serial port trying not to disturb - * any possible real use of the port... - */ -static void serial_console_write(struct console *co, const char *s, - unsigned count) -{ - put_string(sercons_port, s, count); -} - -static struct tty_driver *serial_console_device(struct console *c, int *index) -{ - *index = c->index; - return sci_driver; -} - -/* - * Setup initial baud/bits/parity. We do two things here: - * - construct a cflag setting for the first rs_open() - * - initialize the serial port - * Return non-zero if we didn't find a serial port. - */ -static int __init serial_console_setup(struct console *co, char *options) -{ - int baud = 9600; - int bits = 8; - int parity = 'n'; - int cflag = CREAD | HUPCL | CLOCAL; - char *s; - - sercons_port = &sci_ports[co->index]; - - 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 19200: - cflag |= B19200; - break; - case 38400: - cflag |= B38400; - break; - case 57600: - cflag |= B57600; - break; - case 115200: - cflag |= B115200; - break; - case 9600: - default: - cflag |= B9600; - baud = 9600; - break; - } - switch (bits) { - case 7: - cflag |= CS7; - break; - default: - case 8: - cflag |= CS8; - break; - } - switch (parity) { - case 'o': case 'O': - cflag |= PARODD; - break; - case 'e': case 'E': - cflag |= PARENB; - break; - } - - co->cflag = cflag; - sercons_baud = baud; - -#if defined(__H8300S__) - h8300_sci_enable(sercons_port,sci_enable); -#endif - sci_set_termios_cflag(sercons_port, cflag, baud); - sercons_port->old_cflag = cflag; - - return 0; -} - -static struct console sercons = { - .name = "ttySC", - .write = serial_console_write, - .device = serial_console_device, - .setup = serial_console_setup, - .flags = CON_PRINTBUFFER, - .index = -1, -}; - -/* - * Register console. - */ - -#ifdef CONFIG_SH_EARLY_PRINTK -extern void sh_console_unregister (void); -#endif - -static int __init sci_console_init(void) -{ - register_console(&sercons); -#ifdef CONFIG_SH_EARLY_PRINTK - /* Now that the real console is available, unregister the one we - * used while first booting. - */ - sh_console_unregister(); -#endif - return 0; -} -console_initcall(sci_console_init); - -#endif /* CONFIG_SERIAL_CONSOLE */ - - -#ifdef CONFIG_SH_KGDB - -/* Initialise the KGDB serial port */ -int kgdb_sci_setup(void) -{ - int cflag = CREAD | HUPCL | CLOCAL; - - if ((kgdb_portnum < 0) || (kgdb_portnum >= SCI_NPORTS)) - return -1; - - kgdb_sci_port = &sci_ports[kgdb_portnum]; - - switch (kgdb_baud) { - case 115200: - cflag |= B115200; - break; - case 57600: - cflag |= B57600; - break; - case 38400: - cflag |= B38400; - break; - case 19200: - cflag |= B19200; - break; - case 9600: - default: - cflag |= B9600; - kgdb_baud = 9600; - break; - } - - switch (kgdb_bits) { - case '7': - cflag |= CS7; - break; - default: - case '8': - cflag |= CS8; - break; - } - - switch (kgdb_parity) { - case 'O': - cflag |= PARODD; - break; - case 'E': - cflag |= PARENB; - break; - } - - kgdb_cflag = cflag; - sci_set_termios_cflag(kgdb_sci_port, kgdb_cflag, kgdb_baud); - - /* Set up the interrupt for BREAK from GDB */ - /* Commented out for now since it may not be possible yet... - request_irq(kgdb_sci_port->irqs[0], kgdb_break_interrupt, - SA_INTERRUPT, "sci", kgdb_sci_port); - sci_enable_rx_interrupts(kgdb_sci_port); - */ - - /* Setup complete: initialize function pointers */ - kgdb_getchar = kgdb_sci_getchar; - kgdb_putchar = kgdb_sci_putchar; - - return 0; -} - -#ifdef CONFIG_SH_KGDB_CONSOLE - -/* Create a console device */ -static kdev_t kgdb_console_device(struct console *c) -{ - return MKDEV(SCI_MAJOR, SCI_MINOR_START + c->index); -} - -/* Set up the KGDB console */ -static int __init kgdb_console_setup(struct console *co, char *options) -{ - /* NB we ignore 'options' because we've already done the setup */ - co->cflag = kgdb_cflag; - - return 0; -} - -/* Register the KGDB console so we get messages (d'oh!) */ -void __init kgdb_console_init(void) -{ - register_console(&kgdbcons); -} - -/* The console structure for KGDB */ -static struct console kgdbcons = { - name:"ttySC", - write:kgdb_console_write, - device:kgdb_console_device, - wait_key:serial_console_wait_key, - setup:kgdb_console_setup, - flags:CON_PRINTBUFFER | CON_ENABLED, - index:-1, -}; - -#endif /* CONFIG_SH_KGDB_CONSOLE */ - -#endif /* CONFIG_SH_KGDB */ diff --git a/drivers/char/sh-sci.h b/drivers/char/sh-sci.h deleted file mode 100644 index 5d07cd107..000000000 --- a/drivers/char/sh-sci.h +++ /dev/null @@ -1,478 +0,0 @@ -/* $Id: sh-sci.h,v 1.7 2004/02/10 17:04:17 lethal Exp $ - * - * linux/drivers/char/sh-sci.h - * - * SuperH on-chip serial module support. (SCI with no FIFO / with FIFO) - * Copyright (C) 1999, 2000 Niibe Yutaka - * Copyright (C) 2000 Greg Banks - * Modified to support multiple serial ports. Stuart Menefy (May 2000). - * Modified to support SH7760 SCIF. Paul Mundt (Oct 2003). - * Modified to support H8/300 Serise Yoshinori Sato (Feb 2004). - * - */ -#include - -#if defined(__H8300H__) || defined(__H8300S__) -#include -#if defined(CONFIG_H83007) || defined(CONFIG_H83068) -#include -#endif -#if defined(CONFIG_H8S2678) -#include -#endif -#endif - -/* Values for sci_port->type */ -#define PORT_SCI 0 -#define PORT_SCIF 1 -#define PORT_IRDA 1 /* XXX: temporary assignment */ - -/* Offsets into the sci_port->irqs array */ -#define SCIx_ERI_IRQ 0 -#define SCIx_RXI_IRQ 1 -#define SCIx_TXI_IRQ 2 - -/* ERI, RXI, TXI, BRI */ -#define SCI_IRQS { 23, 24, 25, 0 } -#define SH3_SCIF_IRQS { 56, 57, 59, 58 } -#define SH3_IRDA_IRQS { 52, 53, 55, 54 } -#define SH4_SCIF_IRQS { 40, 41, 43, 42 } -#define STB1_SCIF1_IRQS {23, 24, 26, 25 } -#define SH7760_SCIF0_IRQS { 52, 53, 55, 54 } -#define SH7760_SCIF1_IRQS { 72, 73, 75, 74 } -#define SH7760_SCIF2_IRQS { 76, 77, 79, 78 } -#define H8300H_SCI_IRQS0 {52, 53, 54, 0 } -#define H8300H_SCI_IRQS1 {56, 57, 58, 0 } -#define H8300H_SCI_IRQS2 {60, 61, 62, 0 } -#define H8S_SCI_IRQS0 {88, 89, 90, 0 } -#define H8S_SCI_IRQS1 {92, 93, 94, 0 } -#define H8S_SCI_IRQS2 {96, 97, 98, 0 } - -#if defined(CONFIG_CPU_SUBTYPE_SH7708) -# define SCI_NPORTS 1 -# define SCI_INIT { \ - { {}, PORT_SCI, 0xfffffe80, SCI_IRQS, sci_init_pins_sci } \ -} -# define SCSPTR 0xffffff7c /* 8 bit */ -# define SCSCR_INIT(port) 0x30 /* TIE=0,RIE=0,TE=1,RE=1 */ -# define SCI_ONLY -#elif defined(CONFIG_CPU_SUBTYPE_SH7707) || defined(CONFIG_CPU_SUBTYPE_SH7709) -# define SCI_NPORTS 3 -# define SCI_INIT { \ - { {}, PORT_SCI, 0xfffffe80, SCI_IRQS, sci_init_pins_sci }, \ - { {}, PORT_SCIF, 0xA4000150, SH3_SCIF_IRQS, sci_init_pins_scif }, \ - { {}, PORT_SCIF, 0xA4000140, SH3_IRDA_IRQS, sci_init_pins_irda } \ -} -# define SCPCR 0xA4000116 /* 16 bit SCI and SCIF */ -# define SCPDR 0xA4000136 /* 8 bit SCI and SCIF */ -# define SCSCR_INIT(port) 0x30 /* TIE=0,RIE=0,TE=1,RE=1 */ -# define SCI_AND_SCIF -#elif defined(CONFIG_CPU_SUBTYPE_SH7750) || defined(CONFIG_CPU_SUBTYPE_SH7751) -# define SCI_NPORTS 2 -# define SCI_INIT { \ - { {}, PORT_SCI, 0xffe00000, SCI_IRQS, sci_init_pins_sci }, \ - { {}, PORT_SCIF, 0xFFE80000, SH4_SCIF_IRQS, sci_init_pins_scif } \ -} -# define SCSPTR1 0xffe0001c /* 8 bit SCI */ -# define SCSPTR2 0xFFE80020 /* 16 bit SCIF */ -# define SCIF_ORER 0x0001 /* overrun error bit */ -# define SCSCR_INIT(port) (((port)->type == PORT_SCI) ? \ - 0x30 /* TIE=0,RIE=0,TE=1,RE=1 */ : \ - 0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ ) -# define SCI_AND_SCIF -#elif defined(CONFIG_CPU_SUBTYPE_SH7760) -# define SCI_NPORTS 3 -# define SCI_INIT { \ - { {}, PORT_SCIF, 0xfe600000, SH7760_SCIF0_IRQS, sci_init_pins_scif }, \ - { {}, PORT_SCIF, 0xfe610000, SH7760_SCIF1_IRQS, sci_init_pins_scif }, \ - { {}, PORT_SCIF, 0xfe620000, SH7760_SCIF2_IRQS, sci_init_pins_scif } \ -} -# define SCSPTR0 0xfe600024 /* 16 bit SCIF */ -# define SCSPTR1 0xfe610024 /* 16 bit SCIF */ -# define SCSPTR2 0xfe620024 /* 16 bit SCIF */ -# define SCIF_ORDER 0x0001 /* overrun error bit */ -# define SCSCR_INIT(port) 0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ -# define SCIF_ONLY -#elif defined(CONFIG_CPU_SUBTYPE_ST40STB1) -# define SCI_NPORTS 2 -# define SCI_INIT { \ - { {}, PORT_SCIF, 0xffe00000, STB1_SCIF1_IRQS, sci_init_pins_scif }, \ - { {}, PORT_SCIF, 0xffe80000, SH4_SCIF_IRQS, sci_init_pins_scif } \ -} -# define SCSPTR1 0xffe00020 /* 16 bit SCIF */ -# define SCSPTR2 0xffe80020 /* 16 bit SCIF */ -# define SCIF_ORER 0x0001 /* overrun error bit */ -# define SCSCR_INIT(port) 0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ -# define SCIF_ONLY -#elif defined(CONFIG_H83007) || defined(CONFIG_H83068) -# define SCI_NPORTS 3 -# define SCI_INIT { \ - { {}, PORT_SCI, 0x00ffffb0, H8300H_SCI_IRQS0, sci_init_pins_sci }, \ - { {}, PORT_SCI, 0x00ffffb8, H8300H_SCI_IRQS1, sci_init_pins_sci }, \ - { {}, PORT_SCI, 0x00ffffc0, H8300H_SCI_IRQS2, sci_init_pins_sci } \ -} -# define SCSCR_INIT(port) 0x30 /* TIE=0,RIE=0,TE=1,RE=1 */ -# define SCI_ONLY -# define H8300_SCI_DR(ch) *(volatile char *)(P1DR + h8300_sci_pins[ch].port) -#elif defined(CONFIG_H8S2678) -# define SCI_NPORTS 3 -# define SCI_INIT { \ - { {}, PORT_SCI, 0x00ffff78, H8S_SCI_IRQS0, sci_init_pins_sci }, \ - { {}, PORT_SCI, 0x00ffff80, H8S_SCI_IRQS1, sci_init_pins_sci }, \ - { {}, PORT_SCI, 0x00ffff88, H8S_SCI_IRQS2, sci_init_pins_sci } \ -} -# define SCSCR_INIT(port) 0x30 /* TIE=0,RIE=0,TE=1,RE=1 */ -# define SCI_ONLY -# define H8300_SCI_DR(ch) *(volatile char *)(P1DR + h8300_sci_pins[ch].port) -#else -# error CPU subtype not defined -#endif - -/* SCSCR */ -#define SCI_CTRL_FLAGS_TIE 0x80 /* all */ -#define SCI_CTRL_FLAGS_RIE 0x40 /* all */ -#define SCI_CTRL_FLAGS_TE 0x20 /* all */ -#define SCI_CTRL_FLAGS_RE 0x10 /* all */ -/* SCI_CTRL_FLAGS_REIE 0x08 * 7750 SCIF */ -/* SCI_CTRL_FLAGS_MPIE 0x08 * 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ -/* SCI_CTRL_FLAGS_TEIE 0x04 * 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ -/* SCI_CTRL_FLAGS_CKE1 0x02 * all */ -/* SCI_CTRL_FLAGS_CKE0 0x01 * 7707 SCI/SCIF, 7708 SCI, 7709 SCI/SCIF, 7750 SCI */ - -/* SCxSR SCI */ -#define SCI_TDRE 0x80 /* 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ -#define SCI_RDRF 0x40 /* 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ -#define SCI_ORER 0x20 /* 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ -#define SCI_FER 0x10 /* 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ -#define SCI_PER 0x08 /* 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ -#define SCI_TEND 0x04 /* 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ -/* SCI_MPB 0x02 * 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ -/* SCI_MPBT 0x01 * 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ - -#define SCI_ERRORS ( SCI_PER | SCI_FER | SCI_ORER) - -/* SCxSR SCIF */ -#define SCIF_ER 0x0080 /* 7707 SCIF, 7709 SCIF, 7750 SCIF */ -#define SCIF_TEND 0x0040 /* 7707 SCIF, 7709 SCIF, 7750 SCIF */ -#define SCIF_TDFE 0x0020 /* 7707 SCIF, 7709 SCIF, 7750 SCIF */ -#define SCIF_BRK 0x0010 /* 7707 SCIF, 7709 SCIF, 7750 SCIF */ -#define SCIF_FER 0x0008 /* 7707 SCIF, 7709 SCIF, 7750 SCIF */ -#define SCIF_PER 0x0004 /* 7707 SCIF, 7709 SCIF, 7750 SCIF */ -#define SCIF_RDF 0x0002 /* 7707 SCIF, 7709 SCIF, 7750 SCIF */ -#define SCIF_DR 0x0001 /* 7707 SCIF, 7709 SCIF, 7750 SCIF */ - -#define SCIF_ERRORS ( SCIF_PER | SCIF_FER | SCIF_ER | SCIF_BRK) - -#if defined(SCI_ONLY) -# define SCxSR_TEND(port) SCI_TEND -# define SCxSR_ERRORS(port) SCI_ERRORS -# define SCxSR_RDxF(port) SCI_RDRF -# define SCxSR_TDxE(port) SCI_TDRE -# define SCxSR_ORER(port) SCI_ORER -# define SCxSR_FER(port) SCI_FER -# define SCxSR_PER(port) SCI_PER -# define SCxSR_BRK(port) 0x00 -# define SCxSR_RDxF_CLEAR(port) 0xbc -# define SCxSR_ERROR_CLEAR(port) 0xc4 -# define SCxSR_TDxE_CLEAR(port) 0x78 -# define SCxSR_BREAK_CLEAR(port) 0xc4 -#elif defined(SCIF_ONLY) -# define SCxSR_TEND(port) SCIF_TEND -# define SCxSR_ERRORS(port) SCIF_ERRORS -# define SCxSR_RDxF(port) SCIF_RDF -# define SCxSR_TDxE(port) SCIF_TDFE -# define SCxSR_ORER(port) 0x0000 -# define SCxSR_FER(port) SCIF_FER -# define SCxSR_PER(port) SCIF_PER -# define SCxSR_BRK(port) SCIF_BRK -# define SCxSR_RDxF_CLEAR(port) 0x00fc -# define SCxSR_ERROR_CLEAR(port) 0x0073 -# define SCxSR_TDxE_CLEAR(port) 0x00df -# define SCxSR_BREAK_CLEAR(port) 0x00e3 -#else -# define SCxSR_TEND(port) (((port)->type == PORT_SCI) ? SCI_TEND : SCIF_TEND) -# define SCxSR_ERRORS(port) (((port)->type == PORT_SCI) ? SCI_ERRORS : SCIF_ERRORS) -# define SCxSR_RDxF(port) (((port)->type == PORT_SCI) ? SCI_RDRF : SCIF_RDF) -# define SCxSR_TDxE(port) (((port)->type == PORT_SCI) ? SCI_TDRE : SCIF_TDFE) -# define SCxSR_ORER(port) (((port)->type == PORT_SCI) ? SCI_ORER : 0x0000) -# define SCxSR_FER(port) (((port)->type == PORT_SCI) ? SCI_FER : SCIF_FER) -# define SCxSR_PER(port) (((port)->type == PORT_SCI) ? SCI_PER : SCIF_PER) -# define SCxSR_BRK(port) (((port)->type == PORT_SCI) ? 0x00 : SCIF_BRK) -# define SCxSR_RDxF_CLEAR(port) (((port)->type == PORT_SCI) ? 0xbc : 0x00fc) -# define SCxSR_ERROR_CLEAR(port) (((port)->type == PORT_SCI) ? 0xc4 : 0x0073) -# define SCxSR_TDxE_CLEAR(port) (((port)->type == PORT_SCI) ? 0x78 : 0x00df) -# define SCxSR_BREAK_CLEAR(port) (((port)->type == PORT_SCI) ? 0xc4 : 0x00e3) -#endif - -/* SCFCR */ -#define SCFCR_RFRST 0x0002 -#define SCFCR_TFRST 0x0004 -#define SCFCR_MCE 0x0008 - -#define SCI_MAJOR 204 -#define SCI_MINOR_START 8 - -/* Generic serial flags */ -#define SCI_RX_THROTTLE 0x0000001 - -#define SCI_MAGIC 0xbabeface - -/* - * Events are used to schedule things to happen at timer-interrupt - * time, instead of at rs interrupt time. - */ -#define SCI_EVENT_WRITE_WAKEUP 0 - -struct sci_port { - struct gs_port gs; - int type; - unsigned int base; - unsigned char irqs[4]; /* ERI, RXI, TXI, BRI */ - void (*init_pins)(struct sci_port* port, unsigned int cflag); - unsigned int old_cflag; - struct async_icount icount; - struct work_struct tqueue; - unsigned long event; - int break_flag; -}; - -#define SCI_IN(size, offset) \ - unsigned int addr = port->base + (offset); \ - if ((size) == 8) { \ - return ctrl_inb(addr); \ - } else { \ - return ctrl_inw(addr); \ - } -#define SCI_OUT(size, offset, value) \ - unsigned int addr = port->base + (offset); \ - if ((size) == 8) { \ - ctrl_outb(value, addr); \ - } else { \ - ctrl_outw(value, addr); \ - } - -#define CPU_SCIx_FNS(name, sci_offset, sci_size, scif_offset, scif_size)\ - static inline unsigned int sci_##name##_in(struct sci_port* port) \ - { \ - if (port->type == PORT_SCI) { \ - SCI_IN(sci_size, sci_offset) \ - } else { \ - SCI_IN(scif_size, scif_offset); \ - } \ - } \ - static inline void sci_##name##_out(struct sci_port* port, unsigned int value) \ - { \ - if (port->type == PORT_SCI) { \ - SCI_OUT(sci_size, sci_offset, value) \ - } else { \ - SCI_OUT(scif_size, scif_offset, value); \ - } \ - } - -#define CPU_SCIF_FNS(name, scif_offset, scif_size) \ - static inline unsigned int sci_##name##_in(struct sci_port* port) \ - { \ - SCI_IN(scif_size, scif_offset); \ - } \ - static inline void sci_##name##_out(struct sci_port* port, unsigned int value) \ - { \ - SCI_OUT(scif_size, scif_offset, value); \ - } - -#define CPU_SCI_FNS(name, sci_offset, sci_size) \ - static inline unsigned int sci_##name##_in(struct sci_port* port) \ - { \ - SCI_IN(sci_size, sci_offset); \ - } \ - static inline void sci_##name##_out(struct sci_port* port, unsigned int value) \ - { \ - SCI_OUT(sci_size, sci_offset, value); \ - } - -#ifdef CONFIG_CPU_SH3 -#define SCIx_FNS(name, sh3_sci_offset, sh3_sci_size, sh4_sci_offset, sh4_sci_size, \ - sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size, \ - h8_sci_offset, h8_sci_size) \ - CPU_SCIx_FNS(name, sh3_sci_offset, sh3_sci_size, sh3_scif_offset, sh3_scif_size) -#define SCIF_FNS(name, sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size) \ - CPU_SCIF_FNS(name, sh3_scif_offset, sh3_scif_size) -#elif defined(__H8300H__) || defined(__H8300S__) -#define SCIx_FNS(name, sh3_sci_offset, sh3_sci_size, sh4_sci_offset, sh4_sci_size, \ - sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size, \ - h8_sci_offset, h8_sci_size) \ - CPU_SCI_FNS(name, h8_sci_offset, h8_sci_size) -#define SCIF_FNS(name, sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size) -#else -#define SCIx_FNS(name, sh3_sci_offset, sh3_sci_size, sh4_sci_offset, sh4_sci_size, \ - sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size, \ - h8_sci_offset, h8_sci_size) \ - CPU_SCIx_FNS(name, sh4_sci_offset, sh4_sci_size, sh4_scif_offset, sh4_scif_size) -#define SCIF_FNS(name, sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size) \ - CPU_SCIF_FNS(name, sh4_scif_offset, sh4_scif_size) -#endif - -/* reg SCI/SH3 SCI/SH4 SCIF/SH3 SCIF/SH4 SCI/H8*/ -/* name off sz off sz off sz off sz off sz*/ -SCIx_FNS(SCSMR, 0x00, 8, 0x00, 8, 0x00, 8, 0x00, 16, 0x00, 8) -SCIx_FNS(SCBRR, 0x02, 8, 0x04, 8, 0x02, 8, 0x04, 8, 0x01, 8) -SCIx_FNS(SCSCR, 0x04, 8, 0x08, 8, 0x04, 8, 0x08, 16, 0x02, 8) -SCIx_FNS(SCxTDR, 0x06, 8, 0x0c, 8, 0x06, 8, 0x0C, 8, 0x03, 8) -SCIx_FNS(SCxSR, 0x08, 8, 0x10, 8, 0x08, 16, 0x10, 16, 0x04, 8) -SCIx_FNS(SCxRDR, 0x0a, 8, 0x14, 8, 0x0A, 8, 0x14, 8, 0x05, 8) -SCIF_FNS(SCFCR, 0x0c, 8, 0x18, 16) -SCIF_FNS(SCFDR, 0x0e, 16, 0x1C, 16) -SCIF_FNS(SCLSR, 0, 0, 0x24, 16) - -#define sci_in(port, reg) sci_##reg##_in(port) -#define sci_out(port, reg, value) sci_##reg##_out(port, value) - -/* H8/300 series SCI pins assignment */ -#if defined(__H8300H__) || defined(__H8300S__) -static const struct __attribute__((packed)) -{ - int port; /* GPIO port no */ - unsigned short rx,tx; /* GPIO bit no */ -} h8300_sci_pins[] = -{ -#if defined(CONFIG_H83007) || defined(CONFIG_H83068) - { /* SCI0 */ - .port = H8300_GPIO_P9, - .rx = H8300_GPIO_B2, - .tx = H8300_GPIO_B0, - }, - { /* SCI1 */ - .port = H8300_GPIO_P9, - .rx = H8300_GPIO_B3, - .tx = H8300_GPIO_B1, - }, - { /* SCI2 */ - .port = H8300_GPIO_PB, - .rx = H8300_GPIO_B7, - .tx = H8300_GPIO_B6, - } -#elif defined(CONFIG_H8S2678) - { /* SCI0 */ - .port = H8300_GPIO_P3, - .rx = H8300_GPIO_B2, - .tx = H8300_GPIO_B0, - }, - { /* SCI1 */ - .port = H8300_GPIO_P3, - .rx = H8300_GPIO_B3, - .tx = H8300_GPIO_B1, - }, - { /* SCI2 */ - .port = H8300_GPIO_P5, - .rx = H8300_GPIO_B1, - .tx = H8300_GPIO_B0, - } -#endif -}; -#endif - -#if defined(CONFIG_CPU_SUBTYPE_SH7708) -static inline int sci_rxd_in(struct sci_port *port) -{ - if (port->base == 0xfffffe80) - return ctrl_inb(SCSPTR)&0x01 ? 1 : 0; /* SCI */ - return 1; -} -#elif defined(CONFIG_CPU_SUBTYPE_SH7707) || defined(CONFIG_CPU_SUBTYPE_SH7709) -static inline int sci_rxd_in(struct sci_port *port) -{ - if (port->base == 0xfffffe80) - return ctrl_inb(SCPDR)&0x01 ? 1 : 0; /* SCI */ - if (port->base == 0xa4000150) - return ctrl_inb(SCPDR)&0x10 ? 1 : 0; /* SCIF */ - if (port->base == 0xa4000140) - return ctrl_inb(SCPDR)&0x04 ? 1 : 0; /* IRDA */ - return 1; -} -#elif defined(CONFIG_CPU_SUBTYPE_SH7750) || defined(CONFIG_CPU_SUBTYPE_SH7751) -static inline int sci_rxd_in(struct sci_port *port) -{ -#ifndef SCIF_ONLY - if (port->base == 0xffe00000) - return ctrl_inb(SCSPTR1)&0x01 ? 1 : 0; /* SCI */ -#endif -#ifndef SCI_ONLY - if (port->base == 0xffe80000) - return ctrl_inw(SCSPTR2)&0x0001 ? 1 : 0; /* SCIF */ -#endif - return 1; -} -#elif defined(CONFIG_CPU_SUBTYPE_SH7760) -static inline int sci_rxd_in(struct sci_port *port) -{ - if (port->base == 0xfe600000) - return ctrl_inw(SCSPTR0) & 0x0001 ? 1 : 0; /* SCIF */ - if (port->base == 0xfe610000) - return ctrl_inw(SCSPTR1) & 0x0001 ? 1 : 0; /* SCIF */ - if (port->base == 0xfe620000) - return ctrl_inw(SCSPTR2) & 0x0001 ? 1 : 0; /* SCIF */ -} -#elif defined(CONFIG_CPU_SUBTYPE_ST40STB1) -static inline int sci_rxd_in(struct sci_port *port) -{ - if (port->base == 0xffe00000) - return ctrl_inw(SCSPTR1)&0x0001 ? 1 : 0; /* SCIF */ - else - return ctrl_inw(SCSPTR2)&0x0001 ? 1 : 0; /* SCIF */ - -} -#elif defined(__H8300H__) || defined(__H8300S__) -static inline int sci_rxd_in(struct sci_port *port) -{ - int ch = (port->base - SMR0) >> 3; - return (H8300_SCI_DR(ch) & h8300_sci_pins[ch].rx) ? 1 : 0; -} -#endif - -/* - * Values for the BitRate Register (SCBRR) - * - * The values are actually divisors for a frequency which can - * be internal to the SH3 (14.7456MHz) or derived from an external - * clock source. This driver assumes the internal clock is used; - * to support using an external clock source, config options or - * possibly command-line options would need to be added. - * - * Also, to support speeds below 2400 (why?) the lower 2 bits of - * the SCSMR register would also need to be set to non-zero values. - * - * -- Greg Banks 27Feb2000 - * - * Answer: The SCBRR register is only eight bits, and the value in - * it gets larger with lower baud rates. At around 2400 (depending on - * the peripherial module clock) you run out of bits. However the - * lower two bits of SCSMR allow the module clock to be divided down, - * scaling the value which is needed in SCBRR. - * - * -- Stuart Menefy - 23 May 2000 - * - * I meant, why would anyone bother with bitrates below 2400. - * - * -- Greg Banks - 7Jul2000 - * - * You "speedist"! How will I use my 110bps ASR-33 teletype with paper - * tape reader as a console! - * - * -- Mitch Davis - 15 Jul 2000 - */ - -#define PCLK (current_cpu_data.module_clock) - -#if !defined(__H8300H__) && !defined(__H8300S__) -#define SCBRR_VALUE(bps) ((PCLK+16*bps)/(32*bps)-1) -#else -#define SCBRR_VALUE(bps) (((CONFIG_CPU_CLOCK*1000/32)/bps)-1) -#endif -#define BPS_2400 SCBRR_VALUE(2400) -#define BPS_4800 SCBRR_VALUE(4800) -#define BPS_9600 SCBRR_VALUE(9600) -#define BPS_19200 SCBRR_VALUE(19200) -#define BPS_38400 SCBRR_VALUE(38400) -#define BPS_57600 SCBRR_VALUE(57600) -#define BPS_115200 SCBRR_VALUE(115200) -#define BPS_230400 SCBRR_VALUE(230400) - diff --git a/drivers/char/watchdog/ixp4xx_wdt.c b/drivers/char/watchdog/ixp4xx_wdt.c new file mode 100644 index 000000000..79493650f --- /dev/null +++ b/drivers/char/watchdog/ixp4xx_wdt.c @@ -0,0 +1,233 @@ +/* + * drivers/watchdog/ixp4xx_wdt.c + * + * Watchdog driver for Intel IXP4xx network processors + * + * Author: Deepak Saxena + * + * Copyright 2004 (c) MontaVista, Software, Inc. + * Based on sa1100 driver, Copyright (C) 2000 Oleg Drokin + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef CONFIG_WATCHDOG_NOWAYOUT +static int nowayout = 1; +#else +static int nowayout = 0; +#endif +static int heartbeat = 60; /* (secs) Default is 1 minute */ +static unsigned long wdt_status; +static unsigned long boot_status; + +#define WDT_TICK_RATE (IXP4XX_PERIPHERAL_BUS_CLOCK * 1000000UL) + +#define WDT_IN_USE 0 +#define WDT_OK_TO_CLOSE 1 + +static void +wdt_enable(void) +{ + *IXP4XX_OSWK = IXP4XX_WDT_KEY; + *IXP4XX_OSWE = 0; + *IXP4XX_OSWT = WDT_TICK_RATE * heartbeat; + *IXP4XX_OSWE = IXP4XX_WDT_COUNT_ENABLE | IXP4XX_WDT_RESET_ENABLE; + *IXP4XX_OSWK = 0; +} + +static void +wdt_disable(void) +{ + *IXP4XX_OSWK = IXP4XX_WDT_KEY; + *IXP4XX_OSWE = 0; + *IXP4XX_OSWK = 0; +} + +static int +ixp4xx_wdt_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit(WDT_IN_USE, &wdt_status)) + return -EBUSY; + + clear_bit(WDT_OK_TO_CLOSE, &wdt_status); + + wdt_enable(); + + return 0; +} + +static ssize_t +ixp4xx_wdt_write(struct file *file, const char *data, size_t len, loff_t *ppos) +{ + /* Can't seek (pwrite) on this device */ + if (ppos != &file->f_pos) + return -ESPIPE; + + if (len) { + if (!nowayout) { + size_t i; + + clear_bit(WDT_OK_TO_CLOSE, &wdt_status); + + for (i = 0; i != len; i++) { + char c; + + if (get_user(c, data + i)) + return -EFAULT; + if (c == 'V') + set_bit(WDT_OK_TO_CLOSE, &wdt_status); + } + } + wdt_enable(); + } + + return len; +} + +static struct watchdog_info ident = { + .options = WDIOF_CARDRESET | WDIOF_MAGICCLOSE | + WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, + .identity = "IXP4xx Watchdog", +}; + + +static int +ixp4xx_wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + int ret = -ENOIOCTLCMD; + int time; + + switch (cmd) { + case WDIOC_GETSUPPORT: + ret = copy_to_user((struct watchdog_info *)arg, &ident, + sizeof(ident)) ? -EFAULT : 0; + break; + + case WDIOC_GETSTATUS: + ret = put_user(0, (int *)arg); + break; + + case WDIOC_GETBOOTSTATUS: + ret = put_user(boot_status, (int *)arg); + break; + + case WDIOC_SETTIMEOUT: + ret = get_user(time, (int *)arg); + if (ret) + break; + + if (time <= 0 || time > 60) { + ret = -EINVAL; + break; + } + + heartbeat = time; + wdt_enable(); + /* Fall through */ + + case WDIOC_GETTIMEOUT: + ret = put_user(heartbeat, (int *)arg); + break; + + case WDIOC_KEEPALIVE: + wdt_enable(); + ret = 0; + break; + } + return ret; +} + +static int +ixp4xx_wdt_release(struct inode *inode, struct file *file) +{ + if (test_bit(WDT_OK_TO_CLOSE, &wdt_status)) { + wdt_disable(); + } else { + printk(KERN_CRIT "WATCHDOG: Device closed unexpectdly - " + "timer will not stop\n"); + } + + clear_bit(WDT_IN_USE, &wdt_status); + clear_bit(WDT_OK_TO_CLOSE, &wdt_status); + + return 0; +} + + +static struct file_operations ixp4xx_wdt_fops = +{ + .owner = THIS_MODULE, + .write = ixp4xx_wdt_write, + .ioctl = ixp4xx_wdt_ioctl, + .open = ixp4xx_wdt_open, + .release = ixp4xx_wdt_release, +}; + +static struct miscdevice ixp4xx_wdt_miscdev = +{ + .minor = WATCHDOG_MINOR, + .name = "IXP4xx Watchdog", + .fops = &ixp4xx_wdt_fops, +}; + +static int __init ixp4xx_wdt_init(void) +{ + int ret; + unsigned long processor_id; + + asm("mrc p15, 0, %0, cr0, cr0, 0;" : "=r"(processor_id) :); + if (!(processor_id & 0xf)) { + printk("IXP4XXX Watchdog: Rev. A0 CPU detected - " + "watchdog disabled\n"); + + return -ENODEV; + } + + ret = misc_register(&ixp4xx_wdt_miscdev); + if (ret == 0) + printk("IXP4xx Watchdog Timer: heartbeat %d sec\n", heartbeat); + + boot_status = (*IXP4XX_OSST & IXP4XX_OSST_TIMER_WARM_RESET) ? + WDIOF_CARDRESET : 0; + + return ret; +} + +static void __exit ixp4xx_wdt_exit(void) +{ + misc_deregister(&ixp4xx_wdt_miscdev); +} + + +module_init(ixp4xx_wdt_init); +module_exit(ixp4xx_wdt_exit); + +MODULE_AUTHOR("Deepak Saxena ); +MODULE_DESCRIPTION("IXP4xx Network Processor Watchdog"); + +module_param(heartbeat, int, 0); +MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds (default 60s)"); + +module_param(nowayout, int, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started"); + +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); + diff --git a/drivers/dump/Makefile b/drivers/dump/Makefile new file mode 100644 index 000000000..deb671f77 --- /dev/null +++ b/drivers/dump/Makefile @@ -0,0 +1,16 @@ +# +# Makefile for the dump device drivers. +# + +dump-y := dump_setup.o dump_fmt.o dump_filters.o dump_scheme.o dump_execute.o +dump-$(CONFIG_X86) += dump_i386.o +dump-$(CONFIG_ARM) += dump_arm.o +dump-$(CONFIG_PPC64) += dump_ppc64.o +dump-$(CONFIG_CRASH_DUMP_MEMDEV) += dump_memdev.o dump_overlay.o +dump-objs += $(dump-y) + +obj-$(CONFIG_CRASH_DUMP) += dump.o +obj-$(CONFIG_CRASH_DUMP_BLOCKDEV) += dump_blockdev.o +obj-$(CONFIG_CRASH_DUMP_NETDEV) += dump_netdev.o +obj-$(CONFIG_CRASH_DUMP_COMPRESS_RLE) += dump_rle.o +obj-$(CONFIG_CRASH_DUMP_COMPRESS_GZIP) += dump_gzip.o diff --git a/drivers/dump/dump_arm.c b/drivers/dump/dump_arm.c new file mode 100644 index 000000000..b1849fac6 --- /dev/null +++ b/drivers/dump/dump_arm.c @@ -0,0 +1,250 @@ +/* + * Architecture specific (ARM/XScale) functions for Linux crash dumps. + * + * Created by: Fleming Feng (fleming.feng@intel.com) + * + * Copyright(C) 2003 Intel Corp. All rights reserved. + * + * This code is released under version 2 of the GNU GPL. + */ + +/* + * The hooks for dumping the kernel virtual memory to disk are in this + * file. Any time a modification is made to the virtual memory mechanism, + * these routines must be changed to use the new mechanisms. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static __s32 saved_irq_count; /* saved preempt_count() flags */ + +static int alloc_dha_stack(void) +{ + int i; + void *ptr; + + if (dump_header_asm.dha_stack[0]) + return 0; + + ptr = vmalloc(THREAD_SIZE * num_online_cpus()); + if (!ptr) { + printk("vmalloc for dha_stacks failed\n"); + return -ENOMEM; + } + + for( i = 0; i < num_online_cpus(); i++){ + dump_header_asm.dha_stack[i] = (u32)((unsigned long)ptr + + (i * THREAD_SIZE)); + } + + return 0; +} + +static int free_dha_stack(void) +{ + if (dump_header_asm.dha_stack[0]){ + vfree((void*)dump_header_asm.dha_stack[0]); + dump_header_asm.dha_stack[0] = 0; + } + return 0; +} + +void __dump_save_regs(struct pt_regs* dest_regs, const struct pt_regs* regs) +{ + + /* Here, because the arm version uses _dump_regs_t, + * instead of pt_regs in dump_header_asm, while the + * the function is defined inside architecture independent + * header file include/linux/dump.h, the size of block of + * memory copied is not equal to pt_regs. + */ + + memcpy(dest_regs, regs, sizeof(_dump_regs_t)); + +} + +#ifdef CONFIG_SMP +/* FIXME: This is reserved for possible future usage for SMP system + * based on ARM/XScale. Currently, there is no information for an + * SMP system based on ARM/XScale, they are not used! + */ +/* save registers on other processor */ +void +__dump_save_other_cpus(void) +{ + + /* Dummy now! */ + + return; + +} +#else /* !CONFIG_SMP */ +#define save_other_cpu_state() do { } while (0) +#endif /* !CONFIG_SMP */ + +/* + * Kludge - dump from interrupt context is unreliable (Fixme) + * + * We do this so that softirqs initiated for dump i/o + * get processed and we don't hang while waiting for i/o + * to complete or in any irq synchronization attempt. + * + * This is not quite legal of course, as it has the side + * effect of making all interrupts & softirqs triggered + * while dump is in progress complete before currently + * pending softirqs and the currently executing interrupt + * code. + */ +static inline void +irq_bh_save(void) +{ + saved_irq_count = irq_count(); + preempt_count() &= ~(HARDIRQ_MASK|SOFTIRQ_MASK); +} + +static inline void +irq_bh_restore(void) +{ + preempt_count() |= saved_irq_count; +} + +/* + * Name: __dump_irq_enable + * Func: Reset system so interrupts are enabled. + * This is used for dump methods that requires interrupts + * Eventually, all methods will have interrupts disabled + * and this code can be removed. + * + * Re-enable interrupts + */ +int +__dump_irq_enable(void) +{ + irq_bh_save(); + local_irq_enable(); + return 0; +} + +/* Name: __dump_irq_restore + * Func: Resume the system state in an architecture-specific way. + */ +void +__dump_irq_restore(void) +{ + local_irq_disable(); + irq_bh_restore(); +} + + +/* + * Name: __dump_configure_header() + * Func: Meant to fill in arch specific header fields except per-cpu state + * already captured in dump_lcrash_configure_header. + */ +int +__dump_configure_header(const struct pt_regs *regs) +{ + return (0); +} + +/* + * Name: dump_die_event + * Func: Called from notify_die + */ +static int dump_die_event(struct notifier_block* this, + unsigned long event, + void* arg) +{ + const struct die_args* args = (const struct die_args*)arg; + + switch(event){ + case DIE_PANIC: + case DIE_OOPS: + case DIE_WATCHDOG: + dump_execute(args->str, args->regs); + break; + } + return NOTIFY_DONE; + +} + +static struct notifier_block dump_die_block = { + .notifier_call = dump_die_event, +}; + +/* Name: __dump_init() + * Func: Initialize the dumping routine process. + */ +void +__dump_init(uint64_t local_memory_start) +{ + /* hook into PANIC and OOPS */ + register_die_notifier(&dump_die_block); +} + +/* + * Name: __dump_open() + * Func: Open the dump device (architecture specific). This is in + * case it's necessary in the future. + */ +void +__dump_open(void) +{ + + alloc_dha_stack(); + + return; +} + +/* + * Name: __dump_cleanup() + * Func: Free any architecture specific data structures. This is called + * when the dump module is being removed. + */ +void +__dump_cleanup(void) +{ + free_dha_stack(); + unregister_die_notifier(&dump_die_block); + + /* return */ + return; +} + +/* + * Name: __dump_page_valid() + * Func: Check if page is valid to dump. + */ +int +__dump_page_valid(unsigned long index) +{ + if(!pfn_valid(index)) + return 0; + else + return 1; +} + +/* + * Name: manual_handle_crashdump + * Func: Interface for the lkcd dump command. Calls dump_execute() + */ +int +manual_handle_crashdump(void) { + + _dump_regs_t regs; + + get_current_general_regs(®s); + get_current_cp14_regs(®s); + get_current_cp15_regs(®s); + dump_execute("manual", ®s); + return 0; +} diff --git a/drivers/dump/dump_blockdev.c b/drivers/dump/dump_blockdev.c new file mode 100644 index 000000000..cee31a4ee --- /dev/null +++ b/drivers/dump/dump_blockdev.c @@ -0,0 +1,468 @@ +/* + * Implements the dump driver interface for saving a dump to + * a block device through the kernel's generic low level block i/o + * routines. + * + * Started: June 2002 - Mohamed Abbas + * Moved original lkcd kiobuf dump i/o code from dump_base.c + * to use generic dump device interfaces + * + * Sept 2002 - Bharata B. Rao + * Convert dump i/o to directly use bio instead of kiobuf for 2.5 + * + * Oct 2002 - Suparna Bhattacharya + * Rework to new dumpdev.h structures, implement open/close/ + * silence, misc fixes (blocknr removal, bio_add_page usage) + * + * Copyright (C) 1999 - 2002 Silicon Graphics, Inc. All rights reserved. + * Copyright (C) 2001 - 2002 Matt D. Robinson. All rights reserved. + * Copyright (C) 2002 International Business Machines Corp. + * + * This code is released under version 2 of the GNU GPL. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "dump_methods.h" + +extern void *dump_page_buf; + +/* The end_io callback for dump i/o completion */ +static int +dump_bio_end_io(struct bio *bio, unsigned int bytes_done, int error) +{ + struct dump_blockdev *dump_bdev; + + if (bio->bi_size) { + /* some bytes still left to transfer */ + return 1; /* not complete */ + } + + dump_bdev = (struct dump_blockdev *)bio->bi_private; + if (error) { + printk("IO error while writing the dump, aborting\n"); + } + + dump_bdev->err = error; + + /* no wakeup needed, since caller polls for completion */ + return 0; +} + +/* Check if the dump bio is already mapped to the specified buffer */ +static int +dump_block_map_valid(struct dump_blockdev *dev, struct page *page, + int len) +{ + struct bio *bio = dev->bio; + unsigned long bsize = 0; + + if (!bio->bi_vcnt) + return 0; /* first time, not mapped */ + + + if ((bio_page(bio) != page) || (len > bio->bi_vcnt << PAGE_SHIFT)) + return 0; /* buffer not mapped */ + + bsize = bdev_hardsect_size(bio->bi_bdev); + if ((len & (PAGE_SIZE - 1)) || (len & bsize)) + return 0; /* alignment checks needed */ + + /* quick check to decide if we need to redo bio_add_page */ + if (bdev_get_queue(bio->bi_bdev)->merge_bvec_fn) + return 0; /* device may have other restrictions */ + + return 1; /* already mapped */ +} + +/* + * Set up the dump bio for i/o from the specified buffer + * Return value indicates whether the full buffer could be mapped or not + */ +static int +dump_block_map(struct dump_blockdev *dev, void *buf, int len) +{ + struct page *page = virt_to_page(buf); + struct bio *bio = dev->bio; + unsigned long bsize = 0; + + bio->bi_bdev = dev->bdev; + bio->bi_sector = (dev->start_offset + dev->ddev.curr_offset) >> 9; + bio->bi_idx = 0; /* reset index to the beginning */ + + if (dump_block_map_valid(dev, page, len)) { + /* already mapped and usable rightaway */ + bio->bi_size = len; /* reset size to the whole bio */ + } else { + /* need to map the bio */ + bio->bi_size = 0; + bio->bi_vcnt = 0; + bsize = bdev_hardsect_size(bio->bi_bdev); + + /* first a few sanity checks */ + if (len < bsize) { + printk("map: len less than hardsect size \n"); + return -EINVAL; + } + + if ((unsigned long)buf & bsize) { + printk("map: not aligned \n"); + return -EINVAL; + } + + /* assume contig. page aligned low mem buffer( no vmalloc) */ + if ((page_address(page) != buf) || (len & (PAGE_SIZE - 1))) { + printk("map: invalid buffer alignment!\n"); + return -EINVAL; + } + /* finally we can go ahead and map it */ + while (bio->bi_size < len) + if (bio_add_page(bio, page++, PAGE_SIZE, 0) == 0) { + break; + } + + bio->bi_end_io = dump_bio_end_io; + bio->bi_private = dev; + } + + if (bio->bi_size != len) { + printk("map: bio size = %d not enough for len = %d!\n", + bio->bi_size, len); + return -E2BIG; + } + return 0; +} + +static void +dump_free_bio(struct bio *bio) +{ + if (bio) + kfree(bio->bi_io_vec); + kfree(bio); +} + +/* + * Prepares the dump device so we can take a dump later. + * The caller is expected to have filled up the dev_id field in the + * block dump dev structure. + * + * At dump time when dump_block_write() is invoked it will be too + * late to recover, so as far as possible make sure obvious errors + * get caught right here and reported back to the caller. + */ +static int +dump_block_open(struct dump_dev *dev, unsigned long arg) +{ + struct dump_blockdev *dump_bdev = DUMP_BDEV(dev); + struct block_device *bdev; + int retval = 0; + struct bio_vec *bvec; + + /* make sure this is a valid block device */ + if (!arg) { + retval = -EINVAL; + goto err; + } + + /* Convert it to the new dev_t format */ + arg = MKDEV((arg >> OLDMINORBITS), (arg & OLDMINORMASK)); + + /* get a corresponding block_dev struct for this */ + bdev = bdget((dev_t)arg); + if (!bdev) { + retval = -ENODEV; + goto err; + } + + /* get the block device opened */ + if ((retval = blkdev_get(bdev, O_RDWR | O_LARGEFILE, 0))) { + goto err1; + } + + if ((dump_bdev->bio = kmalloc(sizeof(struct bio), GFP_KERNEL)) + == NULL) { + printk("Cannot allocate bio\n"); + retval = -ENOMEM; + goto err2; + } + + bio_init(dump_bdev->bio); + + if ((bvec = kmalloc(sizeof(struct bio_vec) * + (DUMP_BUFFER_SIZE >> PAGE_SHIFT), GFP_KERNEL)) == NULL) { + retval = -ENOMEM; + goto err3; + } + + /* assign the new dump dev structure */ + dump_bdev->dev_id = (dev_t)arg; + dump_bdev->bdev = bdev; + + /* make a note of the limit */ + dump_bdev->limit = bdev->bd_inode->i_size; + + /* now make sure we can map the dump buffer */ + dump_bdev->bio->bi_io_vec = bvec; + dump_bdev->bio->bi_max_vecs = DUMP_BUFFER_SIZE >> PAGE_SHIFT; + + retval = dump_block_map(dump_bdev, dump_config.dumper->dump_buf, + DUMP_BUFFER_SIZE); + + if (retval) { + printk("open: dump_block_map failed, ret %d\n", retval); + goto err3; + } + + printk("Block device (%d,%d) successfully configured for dumping\n", + MAJOR(dump_bdev->dev_id), + MINOR(dump_bdev->dev_id)); + + + /* after opening the block device, return */ + return retval; + +err3: dump_free_bio(dump_bdev->bio); + dump_bdev->bio = NULL; +err2: if (bdev) blkdev_put(bdev); + goto err; +err1: if (bdev) bdput(bdev); + dump_bdev->bdev = NULL; +err: return retval; +} + +/* + * Close the dump device and release associated resources + * Invoked when unconfiguring the dump device. + */ +static int +dump_block_release(struct dump_dev *dev) +{ + struct dump_blockdev *dump_bdev = DUMP_BDEV(dev); + + /* release earlier bdev if present */ + if (dump_bdev->bdev) { + blkdev_put(dump_bdev->bdev); + dump_bdev->bdev = NULL; + } + + dump_free_bio(dump_bdev->bio); + dump_bdev->bio = NULL; + + return 0; +} + + +/* + * Prepare the dump device for use (silence any ongoing activity + * and quiesce state) when the system crashes. + */ +static int +dump_block_silence(struct dump_dev *dev) +{ + struct dump_blockdev *dump_bdev = DUMP_BDEV(dev); + struct request_queue *q = bdev_get_queue(dump_bdev->bdev); + int ret; + + /* If we can't get request queue lock, refuse to take the dump */ + if (!spin_trylock(q->queue_lock)) + return -EBUSY; + + ret = elv_queue_empty(q); + spin_unlock(q->queue_lock); + + /* For now we assume we have the device to ourselves */ + /* Just a quick sanity check */ + if (!ret) { + /* Warn the user and move on */ + printk(KERN_ALERT "Warning: Non-empty request queue\n"); + printk(KERN_ALERT "I/O requests in flight at dump time\n"); + } + + /* + * Move to a softer level of silencing where no spin_lock_irqs + * are held on other cpus + */ + dump_silence_level = DUMP_SOFT_SPIN_CPUS; + + ret = __dump_irq_enable(); + if (ret) { + return ret; + } + + printk("Dumping to block device (%d,%d) on CPU %d ...\n", + MAJOR(dump_bdev->dev_id), MINOR(dump_bdev->dev_id), + smp_processor_id()); + + return 0; +} + +/* + * Invoked when dumping is done. This is the time to put things back + * (i.e. undo the effects of dump_block_silence) so the device is + * available for normal use. + */ +static int +dump_block_resume(struct dump_dev *dev) +{ + __dump_irq_restore(); + return 0; +} + + +/* + * Seek to the specified offset in the dump device. + * Makes sure this is a valid offset, otherwise returns an error. + */ +static int +dump_block_seek(struct dump_dev *dev, loff_t off) +{ + struct dump_blockdev *dump_bdev = DUMP_BDEV(dev); + loff_t offset = off + dump_bdev->start_offset; + + if (offset & ( PAGE_SIZE - 1)) { + printk("seek: non-page aligned\n"); + return -EINVAL; + } + + if (offset & (bdev_hardsect_size(dump_bdev->bdev) - 1)) { + printk("seek: not sector aligned \n"); + return -EINVAL; + } + + if (offset > dump_bdev->limit) { + printk("seek: not enough space left on device!\n"); + return -ENOSPC; + } + dev->curr_offset = off; + return 0; +} + +/* + * Write out a buffer after checking the device limitations, + * sector sizes, etc. Assumes the buffer is in directly mapped + * kernel address space (not vmalloc'ed). + * + * Returns: number of bytes written or -ERRNO. + */ +static int +dump_block_write(struct dump_dev *dev, void *buf, + unsigned long len) +{ + struct dump_blockdev *dump_bdev = DUMP_BDEV(dev); + loff_t offset = dev->curr_offset + dump_bdev->start_offset; + int retval = -ENOSPC; + + if (offset >= dump_bdev->limit) { + printk("write: not enough space left on device!\n"); + goto out; + } + + /* don't write more blocks than our max limit */ + if (offset + len > dump_bdev->limit) + len = dump_bdev->limit - offset; + + + retval = dump_block_map(dump_bdev, buf, len); + if (retval){ + printk("write: dump_block_map failed! err %d\n", retval); + goto out; + } + + /* + * Write out the data to disk. + * Assumes the entire buffer mapped to a single bio, which we can + * submit and wait for io completion. In the future, may consider + * increasing the dump buffer size and submitting multiple bio s + * for better throughput. + */ + dump_bdev->err = -EAGAIN; + submit_bio(WRITE, dump_bdev->bio); + + dump_bdev->ddev.curr_offset += len; + retval = len; + out: + return retval; +} + +/* + * Name: dump_block_ready() + * Func: check if the last dump i/o is over and ready for next request + */ +static int +dump_block_ready(struct dump_dev *dev, void *buf) +{ + struct dump_blockdev *dump_bdev = DUMP_BDEV(dev); + request_queue_t *q = bdev_get_queue(dump_bdev->bio->bi_bdev); + + /* check for io completion */ + if (dump_bdev->err == -EAGAIN) { + q->unplug_fn(q); + return -EAGAIN; + } + + if (dump_bdev->err) { + printk("dump i/o err\n"); + return dump_bdev->err; + } + + return 0; +} + + +struct dump_dev_ops dump_blockdev_ops = { + .open = dump_block_open, + .release = dump_block_release, + .silence = dump_block_silence, + .resume = dump_block_resume, + .seek = dump_block_seek, + .write = dump_block_write, + /* .read not implemented */ + .ready = dump_block_ready +}; + +static struct dump_blockdev default_dump_blockdev = { + .ddev = {.type_name = "blockdev", .ops = &dump_blockdev_ops, + .curr_offset = 0}, + /* + * leave enough room for the longest swap header possibly written + * written by mkswap (likely the largest page size supported by + * the arch + */ + .start_offset = DUMP_HEADER_OFFSET, + .err = 0 + /* assume the rest of the fields are zeroed by default */ +}; + +struct dump_blockdev *dump_blockdev = &default_dump_blockdev; + +static int __init +dump_blockdev_init(void) +{ + if (dump_register_device(&dump_blockdev->ddev) < 0) { + printk("block device driver registration failed\n"); + return -1; + } + + printk("block device driver for LKCD registered\n"); + return 0; +} + +static void __exit +dump_blockdev_cleanup(void) +{ + dump_unregister_device(&dump_blockdev->ddev); + printk("block device driver for LKCD unregistered\n"); +} + +MODULE_AUTHOR("LKCD Development Team "); +MODULE_DESCRIPTION("Block Dump Driver for Linux Kernel Crash Dump (LKCD)"); +MODULE_LICENSE("GPL"); + +module_init(dump_blockdev_init); +module_exit(dump_blockdev_cleanup); diff --git a/drivers/dump/dump_execute.c b/drivers/dump/dump_execute.c new file mode 100644 index 000000000..d0dc2511c --- /dev/null +++ b/drivers/dump/dump_execute.c @@ -0,0 +1,142 @@ +/* + * The file has the common/generic dump execution code + * + * Started: Oct 2002 - Suparna Bhattacharya + * Split and rewrote high level dump execute code to make use + * of dump method interfaces. + * + * Derived from original code in dump_base.c created by + * Matt Robinson ) + * + * Copyright (C) 1999 - 2002 Silicon Graphics, Inc. All rights reserved. + * Copyright (C) 2001 - 2002 Matt D. Robinson. All rights reserved. + * Copyright (C) 2002 International Business Machines Corp. + * + * Assumes dumper and dump config settings are in place + * (invokes corresponding dumper specific routines as applicable) + * + * This code is released under version 2 of the GNU GPL. + */ +#include +#include +#include +#include +#include +#include "dump_methods.h" + +struct notifier_block *dump_notifier_list; /* dump started/ended callback */ + +extern int panic_timeout; + +/* Dump progress indicator */ +void +dump_speedo(int i) +{ + static const char twiddle[4] = { '|', '\\', '-', '/' }; + printk("%c\b", twiddle[i&3]); +} + +/* Make the device ready and write out the header */ +int dump_begin(void) +{ + int err = 0; + + /* dump_dev = dump_config.dumper->dev; */ + dumper_reset(); + if ((err = dump_dev_silence())) { + /* quiesce failed, can't risk continuing */ + /* Todo/Future: switch to alternate dump scheme if possible */ + printk("dump silence dev failed ! error %d\n", err); + return err; + } + + pr_debug("Writing dump header\n"); + if ((err = dump_update_header())) { + printk("dump update header failed ! error %d\n", err); + dump_dev_resume(); + return err; + } + + dump_config.dumper->curr_offset = DUMP_BUFFER_SIZE; + + return 0; +} + +/* + * Write the dump terminator, a final header update and let go of + * exclusive use of the device for dump. + */ +int dump_complete(void) +{ + int ret = 0; + + if (dump_config.level != DUMP_LEVEL_HEADER) { + if ((ret = dump_update_end_marker())) { + printk("dump update end marker error %d\n", ret); + } + if ((ret = dump_update_header())) { + printk("dump update header error %d\n", ret); + } + } + ret = dump_dev_resume(); + + if ((panic_timeout > 0) && (!(dump_config.flags & (DUMP_FLAGS_SOFTBOOT | DUMP_FLAGS_NONDISRUPT)))) { + printk(KERN_EMERG "Rebooting in %d seconds..",panic_timeout); +#ifdef CONFIG_SMP + smp_send_stop(); +#endif + mdelay(panic_timeout * 1000); + machine_restart(NULL); + } + + return ret; +} + +/* Saves all dump data */ +int dump_execute_savedump(void) +{ + int ret = 0, err = 0; + + if ((ret = dump_begin())) { + return ret; + } + + if (dump_config.level != DUMP_LEVEL_HEADER) { + ret = dump_sequencer(); + } + if ((err = dump_complete())) { + printk("Dump complete failed. Error %d\n", err); + } + + return ret; +} + +extern void dump_calc_bootmap_pages(void); + +/* Does all the real work: Capture and save state */ +int dump_generic_execute(const char *panic_str, const struct pt_regs *regs) +{ + int ret = 0; + + if ((ret = dump_configure_header(panic_str, regs))) { + printk("dump config header failed ! error %d\n", ret); + return ret; + } + + dump_calc_bootmap_pages(); + /* tell interested parties that a dump is about to start */ + notifier_call_chain(&dump_notifier_list, DUMP_BEGIN, + &dump_config.dump_device); + + if (dump_config.level != DUMP_LEVEL_NONE) + ret = dump_execute_savedump(); + + pr_debug("dumped %ld blocks of %d bytes each\n", + dump_config.dumper->count, DUMP_BUFFER_SIZE); + + /* tell interested parties that a dump has completed */ + notifier_call_chain(&dump_notifier_list, DUMP_END, + &dump_config.dump_device); + + return ret; +} diff --git a/drivers/dump/dump_filters.c b/drivers/dump/dump_filters.c new file mode 100644 index 000000000..735cd0bbf --- /dev/null +++ b/drivers/dump/dump_filters.c @@ -0,0 +1,143 @@ +/* + * Default filters to select data to dump for various passes. + * + * Started: Oct 2002 - Suparna Bhattacharya + * Split and rewrote default dump selection logic to generic dump + * method interfaces + * Derived from a portion of dump_base.c created by + * Matt Robinson ) + * + * Copyright (C) 1999 - 2002 Silicon Graphics, Inc. All rights reserved. + * Copyright (C) 2001 - 2002 Matt D. Robinson. All rights reserved. + * Copyright (C) 2002 International Business Machines Corp. + * + * Used during single-stage dumping and during stage 1 of the 2-stage scheme + * (Stage 2 of the 2-stage scheme uses the fully transparent filters + * i.e. passthru filters in dump_overlay.c) + * + * Future: Custom selective dump may involve a different set of filters. + * + * This code is released under version 2 of the GNU GPL. + */ + +#include +#include +#include +#include +#include +#include "dump_methods.h" + +#define DUMP_PFN_SAFETY_MARGIN 1024 /* 4 MB */ +static unsigned long bootmap_pages; + +/* Copied from mm/bootmem.c - FIXME */ +/* return the number of _pages_ that will be allocated for the boot bitmap */ +void dump_calc_bootmap_pages (void) +{ + unsigned long mapsize; + unsigned long pages = num_physpages; + + mapsize = (pages+7)/8; + mapsize = (mapsize + ~PAGE_MASK) & PAGE_MASK; + mapsize >>= PAGE_SHIFT; + bootmap_pages = mapsize + DUMP_PFN_SAFETY_MARGIN + 1; +} + + +/* temporary */ +extern unsigned long min_low_pfn; + + +int dump_low_page(struct page *p) +{ + return ((page_to_pfn(p) >= min_low_pfn) && + (page_to_pfn(p) < (min_low_pfn + bootmap_pages))); +} + +static inline int kernel_page(struct page *p) +{ + /* FIXME: Need to exclude hugetlb pages. Clue: reserved but inuse */ + return (PageReserved(p) && !PageInuse(p)) || (!PageLRU(p) && PageInuse(p)); +} + +static inline int user_page(struct page *p) +{ + return PageInuse(p) && (!PageReserved(p) && PageLRU(p)); +} + +static inline int unreferenced_page(struct page *p) +{ + return !PageInuse(p) && !PageReserved(p); +} + + +/* loc marks the beginning of a range of pages */ +int dump_filter_kernpages(int pass, unsigned long loc, unsigned long sz) +{ + struct page *page = (struct page *)loc; + /* if any of the pages is a kernel page, select this set */ + while (sz) { + if (dump_low_page(page) || kernel_page(page)) + return 1; + sz -= PAGE_SIZE; + page++; + } + return 0; +} + + +/* loc marks the beginning of a range of pages */ +int dump_filter_userpages(int pass, unsigned long loc, unsigned long sz) +{ + struct page *page = (struct page *)loc; + int ret = 0; + /* select if the set has any user page, and no kernel pages */ + while (sz) { + if (user_page(page) && !dump_low_page(page)) { + ret = 1; + } else if (kernel_page(page) || dump_low_page(page)) { + return 0; + } + page++; + sz -= PAGE_SIZE; + } + return ret; +} + + + +/* loc marks the beginning of a range of pages */ +int dump_filter_unusedpages(int pass, unsigned long loc, unsigned long sz) +{ + struct page *page = (struct page *)loc; + + /* select if the set does not have any used pages */ + while (sz) { + if (!unreferenced_page(page) || dump_low_page(page)) { + return 0; + } + page++; + sz -= PAGE_SIZE; + } + return 1; +} + +/* dummy: last (non-existent) pass */ +int dump_filter_none(int pass, unsigned long loc, unsigned long sz) +{ + return 0; +} + +/* TBD: resolve level bitmask ? */ +struct dump_data_filter dump_filter_table[] = { + { .name = "kern", .selector = dump_filter_kernpages, + .level_mask = DUMP_MASK_KERN}, + { .name = "user", .selector = dump_filter_userpages, + .level_mask = DUMP_MASK_USED}, + { .name = "unused", .selector = dump_filter_unusedpages, + .level_mask = DUMP_MASK_UNUSED}, + { .name = "none", .selector = dump_filter_none, + .level_mask = DUMP_MASK_REST}, + { .name = "", .selector = NULL, .level_mask = 0} +}; + diff --git a/drivers/dump/dump_fmt.c b/drivers/dump/dump_fmt.c new file mode 100644 index 000000000..afa0aed56 --- /dev/null +++ b/drivers/dump/dump_fmt.c @@ -0,0 +1,406 @@ +/* + * Implements the routines which handle the format specific + * aspects of dump for the default dump format. + * + * Used in single stage dumping and stage 1 of soft-boot based dumping + * Saves data in LKCD (lcrash) format + * + * Previously a part of dump_base.c + * + * Started: Oct 2002 - Suparna Bhattacharya + * Split off and reshuffled LKCD dump format code around generic + * dump method interfaces. + * + * Derived from original code created by + * Matt Robinson ) + * + * Contributions from SGI, IBM, HP, MCL, and others. + * + * Copyright (C) 1999 - 2002 Silicon Graphics, Inc. All rights reserved. + * Copyright (C) 2000 - 2002 TurboLinux, Inc. All rights reserved. + * Copyright (C) 2001 - 2002 Matt D. Robinson. All rights reserved. + * Copyright (C) 2002 International Business Machines Corp. + * + * This code is released under version 2 of the GNU GPL. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "dump_methods.h" + +/* + * SYSTEM DUMP LAYOUT + * + * System dumps are currently the combination of a dump header and a set + * of data pages which contain the system memory. The layout of the dump + * (for full dumps) is as follows: + * + * +-----------------------------+ + * | generic dump header | + * +-----------------------------+ + * | architecture dump header | + * +-----------------------------+ + * | page header | + * +-----------------------------+ + * | page data | + * +-----------------------------+ + * | page header | + * +-----------------------------+ + * | page data | + * +-----------------------------+ + * | | | + * | | | + * | | | + * | | | + * | V | + * +-----------------------------+ + * | PAGE_END header | + * +-----------------------------+ + * + * There are two dump headers, the first which is architecture + * independent, and the other which is architecture dependent. This + * allows different architectures to dump different data structures + * which are specific to their chipset, CPU, etc. + * + * After the dump headers come a succession of dump page headers along + * with dump pages. The page header contains information about the page + * size, any flags associated with the page (whether it's compressed or + * not), and the address of the page. After the page header is the page + * data, which is either compressed (or not). Each page of data is + * dumped in succession, until the final dump header (PAGE_END) is + * placed at the end of the dump, assuming the dump device isn't out + * of space. + * + * This mechanism allows for multiple compression types, different + * types of data structures, different page ordering, etc., etc., etc. + * It's a very straightforward mechanism for dumping system memory. + */ + +struct __dump_header dump_header; /* the primary dump header */ +struct __dump_header_asm dump_header_asm; /* the arch-specific dump header */ + +/* + * Set up common header fields (mainly the arch indep section) + * Per-cpu state is handled by lcrash_save_context + * Returns the size of the header in bytes. + */ +static int lcrash_init_dump_header(const char *panic_str) +{ + struct timeval dh_time; + unsigned long temp_dha_stack[DUMP_MAX_NUM_CPUS]; + u64 temp_memsz = dump_header.dh_memory_size; + + /* make sure the dump header isn't TOO big */ + if ((sizeof(struct __dump_header) + + sizeof(struct __dump_header_asm)) > DUMP_BUFFER_SIZE) { + printk("lcrash_init_header(): combined " + "headers larger than DUMP_BUFFER_SIZE!\n"); + return -E2BIG; + } + + /* initialize the dump headers to zero */ + /* save dha_stack pointer because it may contains pointer for stack! */ + memcpy(&(temp_dha_stack[0]), &(dump_header_asm.dha_stack[0]), + DUMP_MAX_NUM_CPUS * sizeof(unsigned long)); + memset(&dump_header, 0, sizeof(dump_header)); + memset(&dump_header_asm, 0, sizeof(dump_header_asm)); + dump_header.dh_memory_size = temp_memsz; + memcpy(&(dump_header_asm.dha_stack[0]), &(temp_dha_stack[0]), + DUMP_MAX_NUM_CPUS * sizeof(unsigned long)); + + /* configure dump header values */ + dump_header.dh_magic_number = DUMP_MAGIC_NUMBER; + dump_header.dh_version = DUMP_VERSION_NUMBER; + dump_header.dh_memory_start = PAGE_OFFSET; + dump_header.dh_memory_end = DUMP_MAGIC_NUMBER; + dump_header.dh_header_size = sizeof(struct __dump_header); + dump_header.dh_page_size = PAGE_SIZE; + dump_header.dh_dump_level = dump_config.level; + dump_header.dh_current_task = (unsigned long) current; + dump_header.dh_dump_compress = dump_config.dumper->compress-> + compress_type; + dump_header.dh_dump_flags = dump_config.flags; + dump_header.dh_dump_device = dump_config.dumper->dev->device_id; + +#if DUMP_DEBUG >= 6 + dump_header.dh_num_bytes = 0; +#endif + dump_header.dh_num_dump_pages = 0; + do_gettimeofday(&dh_time); + dump_header.dh_time.tv_sec = dh_time.tv_sec; + dump_header.dh_time.tv_usec = dh_time.tv_usec; + + memcpy((void *)&(dump_header.dh_utsname_sysname), + (const void *)&(system_utsname.sysname), __NEW_UTS_LEN + 1); + memcpy((void *)&(dump_header.dh_utsname_nodename), + (const void *)&(system_utsname.nodename), __NEW_UTS_LEN + 1); + memcpy((void *)&(dump_header.dh_utsname_release), + (const void *)&(system_utsname.release), __NEW_UTS_LEN + 1); + memcpy((void *)&(dump_header.dh_utsname_version), + (const void *)&(system_utsname.version), __NEW_UTS_LEN + 1); + memcpy((void *)&(dump_header.dh_utsname_machine), + (const void *)&(system_utsname.machine), __NEW_UTS_LEN + 1); + memcpy((void *)&(dump_header.dh_utsname_domainname), + (const void *)&(system_utsname.domainname), __NEW_UTS_LEN + 1); + + if (panic_str) { + memcpy((void *)&(dump_header.dh_panic_string), + (const void *)panic_str, DUMP_PANIC_LEN); + } + + dump_header_asm.dha_magic_number = DUMP_ASM_MAGIC_NUMBER; + dump_header_asm.dha_version = DUMP_ASM_VERSION_NUMBER; + dump_header_asm.dha_header_size = sizeof(dump_header_asm); +#ifdef CONFIG_ARM + dump_header_asm.dha_physaddr_start = PHYS_OFFSET; +#endif + + dump_header_asm.dha_smp_num_cpus = num_online_cpus(); + pr_debug("smp_num_cpus in header %d\n", + dump_header_asm.dha_smp_num_cpus); + + dump_header_asm.dha_dumping_cpu = smp_processor_id(); + + return sizeof(dump_header) + sizeof(dump_header_asm); +} + + +int dump_lcrash_configure_header(const char *panic_str, + const struct pt_regs *regs) +{ + int retval = 0; + + dump_config.dumper->header_len = lcrash_init_dump_header(panic_str); + + /* capture register states for all processors */ + dump_save_this_cpu(regs); + __dump_save_other_cpus(); /* side effect:silence cpus */ + + /* configure architecture-specific dump header values */ + if ((retval = __dump_configure_header(regs))) + return retval; + + dump_config.dumper->header_dirty++; + return 0; +} + +/* save register and task context */ +void dump_lcrash_save_context(int cpu, const struct pt_regs *regs, + struct task_struct *tsk) +{ + dump_header_asm.dha_smp_current_task[cpu] = (unsigned long)tsk; + + __dump_save_regs(&dump_header_asm.dha_smp_regs[cpu], regs); + + /* take a snapshot of the stack */ + /* doing this enables us to tolerate slight drifts on this cpu */ + if (dump_header_asm.dha_stack[cpu]) { + memcpy((void *)dump_header_asm.dha_stack[cpu], + tsk->thread_info, THREAD_SIZE); + } + dump_header_asm.dha_stack_ptr[cpu] = (unsigned long)(tsk->thread_info); +} + +/* write out the header */ +int dump_write_header(void) +{ + int retval = 0, size; + void *buf = dump_config.dumper->dump_buf; + + /* accounts for DUMP_HEADER_OFFSET if applicable */ + if ((retval = dump_dev_seek(0))) { + printk("Unable to seek to dump header offset: %d\n", + retval); + return retval; + } + + memcpy(buf, (void *)&dump_header, sizeof(dump_header)); + size = sizeof(dump_header); + memcpy(buf + size, (void *)&dump_header_asm, sizeof(dump_header_asm)); + size += sizeof(dump_header_asm); + size = PAGE_ALIGN(size); + retval = dump_ll_write(buf , size); + + if (retval < size) + return (retval >= 0) ? ENOSPC : retval; + return 0; +} + +int dump_generic_update_header(void) +{ + int err = 0; + + if (dump_config.dumper->header_dirty) { + if ((err = dump_write_header())) { + printk("dump write header failed !err %d\n", err); + } else { + dump_config.dumper->header_dirty = 0; + } + } + + return err; +} + +static inline int is_curr_stack_page(struct page *page, unsigned long size) +{ + unsigned long thread_addr = (unsigned long)current_thread_info(); + unsigned long addr = (unsigned long)page_address(page); + + return !PageHighMem(page) && (addr < thread_addr + THREAD_SIZE) + && (addr + size > thread_addr); +} + +static inline int is_dump_page(struct page *page, unsigned long size) +{ + unsigned long addr = (unsigned long)page_address(page); + unsigned long dump_buf = (unsigned long)dump_config.dumper->dump_buf; + + return !PageHighMem(page) && (addr < dump_buf + DUMP_BUFFER_SIZE) + && (addr + size > dump_buf); +} + +int dump_allow_compress(struct page *page, unsigned long size) +{ + /* + * Don't compress the page if any part of it overlaps + * with the current stack or dump buffer (since the contents + * in these could be changing while compression is going on) + */ + return !is_curr_stack_page(page, size) && !is_dump_page(page, size); +} + +void lcrash_init_pageheader(struct __dump_page *dp, struct page *page, + unsigned long sz) +{ + memset(dp, sizeof(struct __dump_page), 0); + dp->dp_flags = 0; + dp->dp_size = 0; + if (sz > 0) + dp->dp_address = (loff_t)page_to_pfn(page) << PAGE_SHIFT; + +#if DUMP_DEBUG > 6 + dp->dp_page_index = dump_header.dh_num_dump_pages; + dp->dp_byte_offset = dump_header.dh_num_bytes + DUMP_BUFFER_SIZE + + DUMP_HEADER_OFFSET; /* ?? */ +#endif /* DUMP_DEBUG */ +} + +int dump_lcrash_add_data(unsigned long loc, unsigned long len) +{ + struct page *page = (struct page *)loc; + void *addr, *buf = dump_config.dumper->curr_buf; + struct __dump_page *dp = (struct __dump_page *)buf; + int bytes, size; + + if (buf > dump_config.dumper->dump_buf + DUMP_BUFFER_SIZE) + return -ENOMEM; + + lcrash_init_pageheader(dp, page, len); + buf += sizeof(struct __dump_page); + + while (len) { + addr = kmap_atomic(page, KM_DUMP); + size = bytes = (len > PAGE_SIZE) ? PAGE_SIZE : len; + /* check for compression */ + if (dump_allow_compress(page, bytes)) { + size = dump_compress_data((char *)addr, bytes, (char *)buf); + } + /* set the compressed flag if the page did compress */ + if (size && (size < bytes)) { + dp->dp_flags |= DUMP_DH_COMPRESSED; + } else { + /* compression failed -- default to raw mode */ + dp->dp_flags |= DUMP_DH_RAW; + memcpy(buf, addr, bytes); + size = bytes; + } + /* memset(buf, 'A', size); temporary: testing only !! */ + kunmap_atomic(addr, KM_DUMP); + dp->dp_size += size; + buf += size; + len -= bytes; + page++; + } + + /* now update the header */ +#if DUMP_DEBUG > 6 + dump_header.dh_num_bytes += dp->dp_size + sizeof(*dp); +#endif + dump_header.dh_num_dump_pages++; + dump_config.dumper->header_dirty++; + + dump_config.dumper->curr_buf = buf; + + return len; +} + +int dump_lcrash_update_end_marker(void) +{ + struct __dump_page *dp = + (struct __dump_page *)dump_config.dumper->curr_buf; + unsigned long left; + int ret = 0; + + lcrash_init_pageheader(dp, NULL, 0); + dp->dp_flags |= DUMP_DH_END; /* tbd: truncation test ? */ + + /* now update the header */ +#if DUMP_DEBUG > 6 + dump_header.dh_num_bytes += sizeof(*dp); +#endif + dump_config.dumper->curr_buf += sizeof(*dp); + left = dump_config.dumper->curr_buf - dump_config.dumper->dump_buf; + + printk("\n"); + + while (left) { + if ((ret = dump_dev_seek(dump_config.dumper->curr_offset))) { + printk("Seek failed at offset 0x%llx\n", + dump_config.dumper->curr_offset); + return ret; + } + + if (DUMP_BUFFER_SIZE > left) + memset(dump_config.dumper->curr_buf, 'm', + DUMP_BUFFER_SIZE - left); + + if ((ret = dump_ll_write(dump_config.dumper->dump_buf, + DUMP_BUFFER_SIZE)) < DUMP_BUFFER_SIZE) { + return (ret < 0) ? ret : -ENOSPC; + } + + dump_config.dumper->curr_offset += DUMP_BUFFER_SIZE; + + if (left > DUMP_BUFFER_SIZE) { + left -= DUMP_BUFFER_SIZE; + memcpy(dump_config.dumper->dump_buf, + dump_config.dumper->dump_buf + DUMP_BUFFER_SIZE, left); + dump_config.dumper->curr_buf -= DUMP_BUFFER_SIZE; + } else { + left = 0; + } + } + return 0; +} + + +/* Default Formatter (lcrash) */ +struct dump_fmt_ops dump_fmt_lcrash_ops = { + .configure_header = dump_lcrash_configure_header, + .update_header = dump_generic_update_header, + .save_context = dump_lcrash_save_context, + .add_data = dump_lcrash_add_data, + .update_end_marker = dump_lcrash_update_end_marker +}; + +struct dump_fmt dump_fmt_lcrash = { + .name = "lcrash", + .ops = &dump_fmt_lcrash_ops +}; + diff --git a/drivers/dump/dump_gzip.c b/drivers/dump/dump_gzip.c new file mode 100644 index 000000000..8809f5238 --- /dev/null +++ b/drivers/dump/dump_gzip.c @@ -0,0 +1,118 @@ +/* + * GZIP Compression functions for kernel crash dumps. + * + * Created by: Matt Robinson (yakker@sourceforge.net) + * Copyright 2001 Matt D. Robinson. All rights reserved. + * + * This code is released under version 2 of the GNU GPL. + */ + +/* header files */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void *deflate_workspace; + +/* + * Name: dump_compress_gzip() + * Func: Compress a DUMP_PAGE_SIZE page using gzip-style algorithms (the. + * deflate functions similar to what's used in PPP). + */ +static u16 +dump_compress_gzip(const u8 *old, u16 oldsize, u8 *new, u16 newsize) +{ + /* error code and dump stream */ + int err; + z_stream dump_stream; + + dump_stream.workspace = deflate_workspace; + + if ((err = zlib_deflateInit(&dump_stream, Z_BEST_COMPRESSION)) != Z_OK) { + /* fall back to RLE compression */ + printk("dump_compress_gzip(): zlib_deflateInit() " + "failed (%d)!\n", err); + return 0; + } + + /* use old (page of memory) and size (DUMP_PAGE_SIZE) as in-streams */ + dump_stream.next_in = (u8 *) old; + dump_stream.avail_in = oldsize; + + /* out streams are new (dpcpage) and new size (DUMP_DPC_PAGE_SIZE) */ + dump_stream.next_out = new; + dump_stream.avail_out = newsize; + + /* deflate the page -- check for error */ + err = zlib_deflate(&dump_stream, Z_FINISH); + if (err != Z_STREAM_END) { + /* zero is return code here */ + (void)zlib_deflateEnd(&dump_stream); + printk("dump_compress_gzip(): zlib_deflate() failed (%d)!\n", + err); + return 0; + } + + /* let's end the deflated compression stream */ + if ((err = zlib_deflateEnd(&dump_stream)) != Z_OK) { + printk("dump_compress_gzip(): zlib_deflateEnd() " + "failed (%d)!\n", err); + } + + /* return the compressed byte total (if it's smaller) */ + if (dump_stream.total_out >= oldsize) { + return oldsize; + } + return dump_stream.total_out; +} + +/* setup the gzip compression functionality */ +static struct __dump_compress dump_gzip_compression = { + .compress_type = DUMP_COMPRESS_GZIP, + .compress_func = dump_compress_gzip, + .compress_name = "GZIP", +}; + +/* + * Name: dump_compress_gzip_init() + * Func: Initialize gzip as a compression mechanism. + */ +static int __init +dump_compress_gzip_init(void) +{ + deflate_workspace = vmalloc(zlib_deflate_workspacesize()); + if (!deflate_workspace) { + printk("dump_compress_gzip_init(): Failed to " + "alloc %d bytes for deflate workspace\n", + zlib_deflate_workspacesize()); + return -ENOMEM; + } + dump_register_compression(&dump_gzip_compression); + return 0; +} + +/* + * Name: dump_compress_gzip_cleanup() + * Func: Remove gzip as a compression mechanism. + */ +static void __exit +dump_compress_gzip_cleanup(void) +{ + vfree(deflate_workspace); + dump_unregister_compression(DUMP_COMPRESS_GZIP); +} + +/* module initialization */ +module_init(dump_compress_gzip_init); +module_exit(dump_compress_gzip_cleanup); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("LKCD Development Team "); +MODULE_DESCRIPTION("Gzip compression module for crash dump driver"); diff --git a/drivers/dump/dump_i386.c b/drivers/dump/dump_i386.c new file mode 100644 index 000000000..5a01e0f6f --- /dev/null +++ b/drivers/dump/dump_i386.c @@ -0,0 +1,344 @@ +/* + * Architecture specific (i386) functions for Linux crash dumps. + * + * Created by: Matt Robinson (yakker@sgi.com) + * + * Copyright 1999 Silicon Graphics, Inc. All rights reserved. + * + * 2.3 kernel modifications by: Matt D. Robinson (yakker@turbolinux.com) + * Copyright 2000 TurboLinux, Inc. All rights reserved. + * + * This code is released under version 2 of the GNU GPL. + */ + +/* + * The hooks for dumping the kernel virtual memory to disk are in this + * file. Any time a modification is made to the virtual memory mechanism, + * these routines must be changed to use the new mechanisms. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include "dump_methods.h" +#include + +#include +#include +#include +#include + +static __s32 saved_irq_count; /* saved preempt_count() flags */ + +static int +alloc_dha_stack(void) +{ + int i; + void *ptr; + + if (dump_header_asm.dha_stack[0]) + return 0; + + ptr = vmalloc(THREAD_SIZE * num_online_cpus()); + if (!ptr) { + printk("vmalloc for dha_stacks failed\n"); + return -ENOMEM; + } + + for (i = 0; i < num_online_cpus(); i++) { + dump_header_asm.dha_stack[i] = (u32)((unsigned long)ptr + + (i * THREAD_SIZE)); + } + return 0; +} + +static int +free_dha_stack(void) +{ + if (dump_header_asm.dha_stack[0]) { + vfree((void *)dump_header_asm.dha_stack[0]); + dump_header_asm.dha_stack[0] = 0; + } + return 0; +} + + +void +__dump_save_regs(struct pt_regs *dest_regs, const struct pt_regs *regs) +{ + *dest_regs = *regs; + + /* In case of panic dumps, we collects regs on entry to panic. + * so, we shouldn't 'fix' ssesp here again. But it is hard to + * tell just looking at regs whether ssesp need fixing. We make + * this decision by looking at xss in regs. If we have better + * means to determine that ssesp are valid (by some flag which + * tells that we are here due to panic dump), then we can use + * that instead of this kludge. + */ + if (!user_mode(regs)) { + if ((0xffff & regs->xss) == __KERNEL_DS) + /* already fixed up */ + return; + dest_regs->esp = (unsigned long)&(regs->esp); + __asm__ __volatile__ ("movw %%ss, %%ax;" + :"=a"(dest_regs->xss)); + } +} + + +#ifdef CONFIG_SMP +extern cpumask_t irq_affinity[]; +extern irq_desc_t irq_desc[]; +extern void dump_send_ipi(void); + +static int dump_expect_ipi[NR_CPUS]; +static atomic_t waiting_for_dump_ipi; +static cpumask_t saved_affinity[NR_IRQS]; + +extern void stop_this_cpu(void *); /* exported by i386 kernel */ + +static int +dump_nmi_callback(struct pt_regs *regs, int cpu) +{ + if (!dump_expect_ipi[cpu]) + return 0; + + dump_expect_ipi[cpu] = 0; + + dump_save_this_cpu(regs); + atomic_dec(&waiting_for_dump_ipi); + + level_changed: + switch (dump_silence_level) { + case DUMP_HARD_SPIN_CPUS: /* Spin until dump is complete */ + while (dump_oncpu) { + barrier(); /* paranoia */ + if (dump_silence_level != DUMP_HARD_SPIN_CPUS) + goto level_changed; + + cpu_relax(); /* kill time nicely */ + } + break; + + case DUMP_HALT_CPUS: /* Execute halt */ + stop_this_cpu(NULL); + break; + + case DUMP_SOFT_SPIN_CPUS: + /* Mark the task so it spins in schedule */ + set_tsk_thread_flag(current, TIF_NEED_RESCHED); + break; + } + + return 1; +} + +/* save registers on other processors */ +void +__dump_save_other_cpus(void) +{ + int i, cpu = smp_processor_id(); + int other_cpus = num_online_cpus()-1; + + if (other_cpus > 0) { + atomic_set(&waiting_for_dump_ipi, other_cpus); + + for (i = 0; i < NR_CPUS; i++) { + dump_expect_ipi[i] = (i != cpu && cpu_online(i)); + } + + /* short circuit normal NMI handling temporarily */ + set_nmi_callback(dump_nmi_callback); + wmb(); + + dump_send_ipi(); + /* may be we dont need to wait for NMI to be processed. + just write out the header at the end of dumping, if + this IPI is not processed until then, there probably + is a problem and we just fail to capture state of + other cpus. */ + while(atomic_read(&waiting_for_dump_ipi) > 0) { + cpu_relax(); + } + + unset_nmi_callback(); + } +} + +/* + * Routine to save the old irq affinities and change affinities of all irqs to + * the dumping cpu. + */ +static void +set_irq_affinity(void) +{ + int i; + cpumask_t cpu = CPU_MASK_NONE; + + cpu_set(smp_processor_id(), cpu); + memcpy(saved_affinity, irq_affinity, NR_IRQS * sizeof(unsigned long)); + for (i = 0; i < NR_IRQS; i++) { + if (irq_desc[i].handler == NULL) + continue; + irq_affinity[i] = cpu; + if (irq_desc[i].handler->set_affinity != NULL) + irq_desc[i].handler->set_affinity(i, irq_affinity[i]); + } +} + +/* + * Restore old irq affinities. + */ +static void +reset_irq_affinity(void) +{ + int i; + + memcpy(irq_affinity, saved_affinity, NR_IRQS * sizeof(unsigned long)); + for (i = 0; i < NR_IRQS; i++) { + if (irq_desc[i].handler == NULL) + continue; + if (irq_desc[i].handler->set_affinity != NULL) + irq_desc[i].handler->set_affinity(i, saved_affinity[i]); + } +} + +#else /* !CONFIG_SMP */ +#define set_irq_affinity() do { } while (0) +#define reset_irq_affinity() do { } while (0) +#define save_other_cpu_states() do { } while (0) +#endif /* !CONFIG_SMP */ + +/* + * Kludge - dump from interrupt context is unreliable (Fixme) + * + * We do this so that softirqs initiated for dump i/o + * get processed and we don't hang while waiting for i/o + * to complete or in any irq synchronization attempt. + * + * This is not quite legal of course, as it has the side + * effect of making all interrupts & softirqs triggered + * while dump is in progress complete before currently + * pending softirqs and the currently executing interrupt + * code. + */ +static inline void +irq_bh_save(void) +{ + saved_irq_count = irq_count(); + preempt_count() &= ~(HARDIRQ_MASK|SOFTIRQ_MASK); +} + +static inline void +irq_bh_restore(void) +{ + preempt_count() |= saved_irq_count; +} + +/* + * Name: __dump_irq_enable + * Func: Reset system so interrupts are enabled. + * This is used for dump methods that require interrupts + * Eventually, all methods will have interrupts disabled + * and this code can be removed. + * + * Change irq affinities + * Re-enable interrupts + */ +int +__dump_irq_enable(void) +{ + set_irq_affinity(); + irq_bh_save(); + local_irq_enable(); + return 0; +} + +/* + * Name: __dump_irq_restore + * Func: Resume the system state in an architecture-specific way. + + */ +void +__dump_irq_restore(void) +{ + local_irq_disable(); + reset_irq_affinity(); + irq_bh_restore(); +} + +/* + * Name: __dump_configure_header() + * Func: Meant to fill in arch specific header fields except per-cpu state + * already captured via __dump_save_context for all CPUs. + */ +int +__dump_configure_header(const struct pt_regs *regs) +{ + return (0); +} + +/* + * Name: __dump_init() + * Func: Initialize the dumping routine process. + */ +void +__dump_init(uint64_t local_memory_start) +{ + return; +} + +/* + * Name: __dump_open() + * Func: Open the dump device (architecture specific). + */ +void +__dump_open(void) +{ + alloc_dha_stack(); +} + +/* + * Name: __dump_cleanup() + * Func: Free any architecture specific data structures. This is called + * when the dump module is being removed. + */ +void +__dump_cleanup(void) +{ + free_dha_stack(); +} + +extern int pfn_is_ram(unsigned long); + +/* + * Name: __dump_page_valid() + * Func: Check if page is valid to dump. + */ +int +__dump_page_valid(unsigned long index) +{ + if (!pfn_valid(index)) + return 0; + + return pfn_is_ram(index); +} + +/* + * Name: manual_handle_crashdump() + * Func: Interface for the lkcd dump command. Calls dump_execute() + */ +int +manual_handle_crashdump(void) { + + struct pt_regs regs; + + get_current_regs(®s); + dump_execute("manual", ®s); + return 0; +} diff --git a/drivers/dump/dump_memdev.c b/drivers/dump/dump_memdev.c new file mode 100644 index 000000000..1cd700d31 --- /dev/null +++ b/drivers/dump/dump_memdev.c @@ -0,0 +1,640 @@ +/* + * Implements the dump driver interface for saving a dump in available + * memory areas. The saved pages may be written out to persistent storage + * after a soft reboot. + * + * Started: Oct 2002 - Suparna Bhattacharya + * + * Copyright (C) 2002 International Business Machines Corp. + * + * This code is released under version 2 of the GNU GPL. + * + * The approach of tracking pages containing saved dump using map pages + * allocated as needed has been derived from the Mission Critical Linux + * mcore dump implementation. + * + * Credits and a big thanks for letting the lkcd project make use of + * the excellent piece of work and also helping with clarifications + * and tips along the way are due to: + * Dave Winchell (primary author of mcore) + * Jeff Moyer + * Josh Huber + * + * For those familiar with the mcore code, the main differences worth + * noting here (besides the dump device abstraction) result from enabling + * "high" memory pages (pages not permanently mapped in the kernel + * address space) to be used for saving dump data (because of which a + * simple virtual address based linked list cannot be used anymore for + * managing free pages), an added level of indirection for faster + * lookups during the post-boot stage, and the idea of pages being + * made available as they get freed up while dump to memory progresses + * rather than one time before starting the dump. The last point enables + * a full memory snapshot to be saved starting with an initial set of + * bootstrap pages given a good compression ratio. (See dump_overlay.c) + * + */ + +/* + * -----------------MEMORY LAYOUT ------------------ + * The memory space consists of a set of discontiguous pages, and + * discontiguous map pages as well, rooted in a chain of indirect + * map pages (also discontiguous). Except for the indirect maps + * (which must be preallocated in advance), the rest of the pages + * could be in high memory. + * + * root + * | --------- -------- -------- + * --> | . . +|--->| . +|------->| . . | indirect + * --|--|--- ---|---- --|-|--- maps + * | | | | | + * ------ ------ ------- ------ ------- + * | . | | . | | . . | | . | | . . | maps + * --|--- --|--- --|--|-- --|--- ---|-|-- + * page page page page page page page data + * pages + * + * Writes to the dump device happen sequentially in append mode. + * The main reason for the existence of the indirect map is + * to enable a quick way to lookup a specific logical offset in + * the saved data post-soft-boot, e.g. to writeout pages + * with more critical data first, even though such pages + * would have been compressed and copied last, being the lowest + * ranked candidates for reuse due to their criticality. + * (See dump_overlay.c) + */ +#include +#include +#include +#include +#include "dump_methods.h" + +#define DUMP_MAP_SZ (PAGE_SIZE / sizeof(unsigned long)) /* direct map size */ +#define DUMP_IND_MAP_SZ DUMP_MAP_SZ - 1 /* indirect map size */ +#define DUMP_NR_BOOTSTRAP 64 /* no of bootstrap pages */ + +extern int dump_low_page(struct page *); + +/* check if the next entry crosses a page boundary */ +static inline int is_last_map_entry(unsigned long *map) +{ + unsigned long addr = (unsigned long)(map + 1); + + return (!(addr & (PAGE_SIZE - 1))); +} + +/* Todo: should have some validation checks */ +/* The last entry in the indirect map points to the next indirect map */ +/* Indirect maps are referred to directly by virtual address */ +static inline unsigned long *next_indirect_map(unsigned long *map) +{ + return (unsigned long *)map[DUMP_IND_MAP_SZ]; +} + +#ifdef CONFIG_CRASH_DUMP_SOFTBOOT +/* Called during early bootup - fixme: make this __init */ +void dump_early_reserve_map(struct dump_memdev *dev) +{ + unsigned long *map1, *map2; + loff_t off = 0, last = dev->last_used_offset >> PAGE_SHIFT; + int i, j; + + printk("Reserve bootmap space holding previous dump of %lld pages\n", + last); + map1= (unsigned long *)dev->indirect_map_root; + + while (map1 && (off < last)) { + reserve_bootmem(virt_to_phys((void *)map1), PAGE_SIZE); + for (i=0; (i < DUMP_MAP_SZ - 1) && map1[i] && (off < last); + i++, off += DUMP_MAP_SZ) { + pr_debug("indirect map[%d] = 0x%lx\n", i, map1[i]); + if (map1[i] >= max_low_pfn) + continue; + reserve_bootmem(map1[i] << PAGE_SHIFT, PAGE_SIZE); + map2 = pfn_to_kaddr(map1[i]); + for (j = 0 ; (j < DUMP_MAP_SZ) && map2[j] && + (off + j < last); j++) { + pr_debug("\t map[%d][%d] = 0x%lx\n", i, j, + map2[j]); + if (map2[j] < max_low_pfn) { + reserve_bootmem(map2[j] << PAGE_SHIFT, + PAGE_SIZE); + } + } + } + map1 = next_indirect_map(map1); + } + dev->nr_free = 0; /* these pages don't belong to this boot */ +} +#endif + +/* mark dump pages so that they aren't used by this kernel */ +void dump_mark_map(struct dump_memdev *dev) +{ + unsigned long *map1, *map2; + loff_t off = 0, last = dev->last_used_offset >> PAGE_SHIFT; + struct page *page; + int i, j; + + printk("Dump: marking pages in use by previous dump\n"); + map1= (unsigned long *)dev->indirect_map_root; + + while (map1 && (off < last)) { + page = virt_to_page(map1); + set_page_count(page, 1); + for (i=0; (i < DUMP_MAP_SZ - 1) && map1[i] && (off < last); + i++, off += DUMP_MAP_SZ) { + pr_debug("indirect map[%d] = 0x%lx\n", i, map1[i]); + page = pfn_to_page(map1[i]); + set_page_count(page, 1); + map2 = kmap_atomic(page, KM_DUMP); + for (j = 0 ; (j < DUMP_MAP_SZ) && map2[j] && + (off + j < last); j++) { + pr_debug("\t map[%d][%d] = 0x%lx\n", i, j, + map2[j]); + page = pfn_to_page(map2[j]); + set_page_count(page, 1); + } + } + map1 = next_indirect_map(map1); + } +} + + +/* + * Given a logical offset into the mem device lookup the + * corresponding page + * loc is specified in units of pages + * Note: affects curr_map (even in the case where lookup fails) + */ +struct page *dump_mem_lookup(struct dump_memdev *dump_mdev, unsigned long loc) +{ + unsigned long *map; + unsigned long i, index = loc / DUMP_MAP_SZ; + struct page *page = NULL; + unsigned long curr_pfn, curr_map, *curr_map_ptr = NULL; + + map = (unsigned long *)dump_mdev->indirect_map_root; + if (!map) + return NULL; + + if (loc > dump_mdev->last_offset >> PAGE_SHIFT) + return NULL; + + /* + * first locate the right indirect map + * in the chain of indirect maps + */ + for (i = 0; i + DUMP_IND_MAP_SZ < index ; i += DUMP_IND_MAP_SZ) { + if (!(map = next_indirect_map(map))) + return NULL; + } + /* then the right direct map */ + /* map entries are referred to by page index */ + if ((curr_map = map[index - i])) { + page = pfn_to_page(curr_map); + /* update the current traversal index */ + /* dump_mdev->curr_map = &map[index - i];*/ + curr_map_ptr = &map[index - i]; + } + + if (page) + map = kmap_atomic(page, KM_DUMP); + else + return NULL; + + /* and finally the right entry therein */ + /* data pages are referred to by page index */ + i = index * DUMP_MAP_SZ; + if ((curr_pfn = map[loc - i])) { + page = pfn_to_page(curr_pfn); + dump_mdev->curr_map = curr_map_ptr; + dump_mdev->curr_map_offset = loc - i; + dump_mdev->ddev.curr_offset = loc << PAGE_SHIFT; + } else { + page = NULL; + } + kunmap_atomic(map, KM_DUMP); + + return page; +} + +/* + * Retrieves a pointer to the next page in the dump device + * Used during the lookup pass post-soft-reboot + */ +struct page *dump_mem_next_page(struct dump_memdev *dev) +{ + unsigned long i; + unsigned long *map; + struct page *page = NULL; + + if (dev->ddev.curr_offset + PAGE_SIZE >= dev->last_offset) { + return NULL; + } + + if ((i = (unsigned long)(++dev->curr_map_offset)) >= DUMP_MAP_SZ) { + /* move to next map */ + if (is_last_map_entry(++dev->curr_map)) { + /* move to the next indirect map page */ + printk("dump_mem_next_page: go to next indirect map\n"); + dev->curr_map = (unsigned long *)*dev->curr_map; + if (!dev->curr_map) + return NULL; + } + i = dev->curr_map_offset = 0; + pr_debug("dump_mem_next_page: next map 0x%lx, entry 0x%lx\n", + dev->curr_map, *dev->curr_map); + + }; + + if (*dev->curr_map) { + map = kmap_atomic(pfn_to_page(*dev->curr_map), KM_DUMP); + if (map[i]) + page = pfn_to_page(map[i]); + kunmap_atomic(map, KM_DUMP); + dev->ddev.curr_offset += PAGE_SIZE; + }; + + return page; +} + +/* Copied from dump_filters.c */ +static inline int kernel_page(struct page *p) +{ + /* FIXME: Need to exclude hugetlb pages. Clue: reserved but inuse */ + return (PageReserved(p) && !PageInuse(p)) || (!PageLRU(p) && PageInuse(p)); +} + +static inline int user_page(struct page *p) +{ + return PageInuse(p) && (!PageReserved(p) && PageLRU(p)); +} + +int dump_reused_by_boot(struct page *page) +{ + /* Todo + * Checks: + * if PageReserved + * if < __end + bootmem_bootmap_pages for this boot + allowance + * if overwritten by initrd (how to check ?) + * Also, add more checks in early boot code + * e.g. bootmem bootmap alloc verify not overwriting dump, and if + * so then realloc or move the dump pages out accordingly. + */ + + /* Temporary proof of concept hack, avoid overwriting kern pages */ + + return (kernel_page(page) || dump_low_page(page) || user_page(page)); +} + + +/* Uses the free page passed in to expand available space */ +int dump_mem_add_space(struct dump_memdev *dev, struct page *page) +{ + struct page *map_page; + unsigned long *map; + unsigned long i; + + if (!dev->curr_map) + return -ENOMEM; /* must've exhausted indirect map */ + + if (!*dev->curr_map || dev->curr_map_offset >= DUMP_MAP_SZ) { + /* add map space */ + *dev->curr_map = page_to_pfn(page); + dev->curr_map_offset = 0; + return 0; + } + + /* add data space */ + i = dev->curr_map_offset; + map_page = pfn_to_page(*dev->curr_map); + map = (unsigned long *)kmap_atomic(map_page, KM_DUMP); + map[i] = page_to_pfn(page); + kunmap_atomic(map, KM_DUMP); + dev->curr_map_offset = ++i; + dev->last_offset += PAGE_SIZE; + if (i >= DUMP_MAP_SZ) { + /* move to next map */ + if (is_last_map_entry(++dev->curr_map)) { + /* move to the next indirect map page */ + pr_debug("dump_mem_add_space: using next" + "indirect map\n"); + dev->curr_map = (unsigned long *)*dev->curr_map; + } + } + return 0; +} + + +/* Caution: making a dest page invalidates existing contents of the page */ +int dump_check_and_free_page(struct dump_memdev *dev, struct page *page) +{ + int err = 0; + + /* + * the page can be used as a destination only if we are sure + * it won't get overwritten by the soft-boot, and is not + * critical for us right now. + */ + if (dump_reused_by_boot(page)) + return 0; + + if ((err = dump_mem_add_space(dev, page))) { + printk("Warning: Unable to extend memdev space. Err %d\n", + err); + return 0; + } + + dev->nr_free++; + return 1; +} + + +/* Set up the initial maps and bootstrap space */ +/* Must be called only after any previous dump is written out */ +int dump_mem_open(struct dump_dev *dev, unsigned long devid) +{ + struct dump_memdev *dump_mdev = DUMP_MDEV(dev); + unsigned long nr_maps, *map, *prev_map = &dump_mdev->indirect_map_root; + void *addr; + struct page *page; + unsigned long i = 0; + int err = 0; + + /* Todo: sanity check for unwritten previous dump */ + + /* allocate pages for indirect map (non highmem area) */ + nr_maps = num_physpages / DUMP_MAP_SZ; /* maps to cover entire mem */ + for (i = 0; i < nr_maps; i += DUMP_IND_MAP_SZ) { + if (!(map = (unsigned long *)dump_alloc_mem(PAGE_SIZE))) { + printk("Unable to alloc indirect map %ld\n", + i / DUMP_IND_MAP_SZ); + return -ENOMEM; + } + clear_page(map); + *prev_map = (unsigned long)map; + prev_map = &map[DUMP_IND_MAP_SZ]; + }; + + dump_mdev->curr_map = (unsigned long *)dump_mdev->indirect_map_root; + dump_mdev->curr_map_offset = 0; + + /* + * allocate a few bootstrap pages: at least 1 map and 1 data page + * plus enough to save the dump header + */ + i = 0; + do { + if (!(addr = dump_alloc_mem(PAGE_SIZE))) { + printk("Unable to alloc bootstrap page %ld\n", i); + return -ENOMEM; + } + + page = virt_to_page(addr); + if (dump_low_page(page)) { + dump_free_mem(addr); + continue; + } + + if (dump_mem_add_space(dump_mdev, page)) { + printk("Warning: Unable to extend memdev " + "space. Err %d\n", err); + dump_free_mem(addr); + continue; + } + i++; + } while (i < DUMP_NR_BOOTSTRAP); + + printk("dump memdev init: %ld maps, %ld bootstrap pgs, %ld free pgs\n", + nr_maps, i, dump_mdev->last_offset >> PAGE_SHIFT); + + dump_mdev->last_bs_offset = dump_mdev->last_offset; + + return 0; +} + +/* Releases all pre-alloc'd pages */ +int dump_mem_release(struct dump_dev *dev) +{ + struct dump_memdev *dump_mdev = DUMP_MDEV(dev); + struct page *page, *map_page; + unsigned long *map, *prev_map; + void *addr; + int i; + + if (!dump_mdev->nr_free) + return 0; + + pr_debug("dump_mem_release\n"); + page = dump_mem_lookup(dump_mdev, 0); + for (i = 0; page && (i < DUMP_NR_BOOTSTRAP - 1); i++) { + if (PageHighMem(page)) + break; + addr = page_address(page); + if (!addr) { + printk("page_address(%p) = NULL\n", page); + break; + } + pr_debug("Freeing page at 0x%lx\n", addr); + dump_free_mem(addr); + if (dump_mdev->curr_map_offset >= DUMP_MAP_SZ - 1) { + map_page = pfn_to_page(*dump_mdev->curr_map); + if (PageHighMem(map_page)) + break; + page = dump_mem_next_page(dump_mdev); + addr = page_address(map_page); + if (!addr) { + printk("page_address(%p) = NULL\n", + map_page); + break; + } + pr_debug("Freeing map page at 0x%lx\n", addr); + dump_free_mem(addr); + i++; + } else { + page = dump_mem_next_page(dump_mdev); + } + } + + /* now for the last used bootstrap page used as a map page */ + if ((i < DUMP_NR_BOOTSTRAP) && (*dump_mdev->curr_map)) { + map_page = pfn_to_page(*dump_mdev->curr_map); + if ((map_page) && !PageHighMem(map_page)) { + addr = page_address(map_page); + if (!addr) { + printk("page_address(%p) = NULL\n", map_page); + } else { + pr_debug("Freeing map page at 0x%lx\n", addr); + dump_free_mem(addr); + i++; + } + } + } + + printk("Freed %d bootstrap pages\n", i); + + /* free the indirect maps */ + map = (unsigned long *)dump_mdev->indirect_map_root; + + i = 0; + while (map) { + prev_map = map; + map = next_indirect_map(map); + dump_free_mem(prev_map); + i++; + } + + printk("Freed %d indirect map(s)\n", i); + + /* Reset the indirect map */ + dump_mdev->indirect_map_root = 0; + dump_mdev->curr_map = 0; + + /* Reset the free list */ + dump_mdev->nr_free = 0; + + dump_mdev->last_offset = dump_mdev->ddev.curr_offset = 0; + dump_mdev->last_used_offset = 0; + dump_mdev->curr_map = NULL; + dump_mdev->curr_map_offset = 0; + return 0; +} + +/* + * Long term: + * It is critical for this to be very strict. Cannot afford + * to have anything running and accessing memory while we overwrite + * memory (potential risk of data corruption). + * If in doubt (e.g if a cpu is hung and not responding) just give + * up and refuse to proceed with this scheme. + * + * Note: I/O will only happen after soft-boot/switchover, so we can + * safely disable interrupts and force stop other CPUs if this is + * going to be a disruptive dump, no matter what they + * are in the middle of. + */ +/* + * ATM Most of this is already taken care of in the nmi handler + * We may halt the cpus rightaway if we know this is going to be disruptive + * For now, since we've limited ourselves to overwriting free pages we + * aren't doing much here. Eventually, we'd have to wait to make sure other + * cpus aren't using memory we could be overwriting + */ +int dump_mem_silence(struct dump_dev *dev) +{ + struct dump_memdev *dump_mdev = DUMP_MDEV(dev); + + if (dump_mdev->last_offset > dump_mdev->last_bs_offset) { + /* prefer to run lkcd config & start with a clean slate */ + return -EEXIST; + } + return 0; +} + +extern int dump_overlay_resume(void); + +/* Trigger the next stage of dumping */ +int dump_mem_resume(struct dump_dev *dev) +{ + dump_overlay_resume(); + return 0; +} + +/* + * Allocate mem dev pages as required and copy buffer contents into it. + * Fails if the no free pages are available + * Keeping it simple and limited for starters (can modify this over time) + * Does not handle holes or a sparse layout + * Data must be in multiples of PAGE_SIZE + */ +int dump_mem_write(struct dump_dev *dev, void *buf, unsigned long len) +{ + struct dump_memdev *dump_mdev = DUMP_MDEV(dev); + struct page *page; + unsigned long n = 0; + void *addr; + unsigned long *saved_curr_map, saved_map_offset; + int ret = 0; + + pr_debug("dump_mem_write: offset 0x%llx, size %ld\n", + dev->curr_offset, len); + + if (dev->curr_offset + len > dump_mdev->last_offset) { + printk("Out of space to write\n"); + return -ENOSPC; + } + + if ((len & (PAGE_SIZE - 1)) || (dev->curr_offset & (PAGE_SIZE - 1))) + return -EINVAL; /* not aligned in units of page size */ + + saved_curr_map = dump_mdev->curr_map; + saved_map_offset = dump_mdev->curr_map_offset; + page = dump_mem_lookup(dump_mdev, dev->curr_offset >> PAGE_SHIFT); + + for (n = len; (n > 0) && page; n -= PAGE_SIZE, buf += PAGE_SIZE ) { + addr = kmap_atomic(page, KM_DUMP); + /* memset(addr, 'x', PAGE_SIZE); */ + memcpy(addr, buf, PAGE_SIZE); + kunmap_atomic(addr, KM_DUMP); + /* dev->curr_offset += PAGE_SIZE; */ + page = dump_mem_next_page(dump_mdev); + } + + dump_mdev->curr_map = saved_curr_map; + dump_mdev->curr_map_offset = saved_map_offset; + + if (dump_mdev->last_used_offset < dev->curr_offset) + dump_mdev->last_used_offset = dev->curr_offset; + + return (len - n) ? (len - n) : ret ; +} + +/* dummy - always ready */ +int dump_mem_ready(struct dump_dev *dev, void *buf) +{ + return 0; +} + +/* + * Should check for availability of space to write upto the offset + * affects only the curr_offset; last_offset untouched + * Keep it simple: Only allow multiples of PAGE_SIZE for now + */ +int dump_mem_seek(struct dump_dev *dev, loff_t offset) +{ + struct dump_memdev *dump_mdev = DUMP_MDEV(dev); + + if (offset & (PAGE_SIZE - 1)) + return -EINVAL; /* allow page size units only for now */ + + /* Are we exceeding available space ? */ + if (offset > dump_mdev->last_offset) { + printk("dump_mem_seek failed for offset 0x%llx\n", + offset); + return -ENOSPC; + } + + dump_mdev->ddev.curr_offset = offset; + return 0; +} + +struct dump_dev_ops dump_memdev_ops = { + .open = dump_mem_open, + .release = dump_mem_release, + .silence = dump_mem_silence, + .resume = dump_mem_resume, + .seek = dump_mem_seek, + .write = dump_mem_write, + .read = NULL, /* not implemented at the moment */ + .ready = dump_mem_ready +}; + +static struct dump_memdev default_dump_memdev = { + .ddev = {.type_name = "memdev", .ops = &dump_memdev_ops, + .device_id = 0x14} + /* assume the rest of the fields are zeroed by default */ +}; + +/* may be overwritten if a previous dump exists */ +struct dump_memdev *dump_memdev = &default_dump_memdev; + diff --git a/drivers/dump/dump_methods.h b/drivers/dump/dump_methods.h new file mode 100644 index 000000000..d2e1f7c02 --- /dev/null +++ b/drivers/dump/dump_methods.h @@ -0,0 +1,349 @@ +/* + * Generic interfaces for flexible system dump + * + * Started: Oct 2002 - Suparna Bhattacharya (suparna@in.ibm.com) + * + * Copyright (C) 2002 International Business Machines Corp. + * + * This code is released under version 2 of the GNU GPL. + */ + +#ifndef _LINUX_DUMP_METHODS_H +#define _LINUX_DUMP_METHODS_H + +/* + * Inspired by Matt Robinson's suggestion of introducing dump + * methods as a way to enable different crash dump facilities to + * coexist where each employs its own scheme or dumping policy. + * + * The code here creates a framework for flexible dump by defining + * a set of methods and providing associated helpers that differentiate + * between the underlying mechanism (how to dump), overall scheme + * (sequencing of stages and data dumped and associated quiescing), + * output format (what the dump output looks like), target type + * (where to save the dump; see dumpdev.h), and selection policy + * (state/data to dump). + * + * These sets of interfaces can be mixed and matched to build a + * dumper suitable for a given situation, allowing for + * flexibility as well appropriate degree of code reuse. + * For example all features and options of lkcd (including + * granular selective dumping in the near future) should be + * available even when say, the 2 stage soft-boot based mechanism + * is used for taking disruptive dumps. + * + * Todo: Additionally modules or drivers may supply their own + * custom dumpers which extend dump with module specific + * information or hardware state, and can even tweak the + * mechanism when it comes to saving state relevant to + * them. + */ + +#include +#include +#include +#include + +#define MAX_PASSES 6 +#define MAX_DEVS 4 + + +/* To customise selection of pages to be dumped in a given pass/group */ +struct dump_data_filter{ + char name[32]; + int (*selector)(int, unsigned long, unsigned long); + ulong level_mask; /* dump level(s) for which this filter applies */ + loff_t start[MAX_NUMNODES], end[MAX_NUMNODES]; /* location range applicable */ + ulong num_mbanks; /* Number of memory banks. Greater than one for discontig memory (NUMA) */ +}; + + +/* + * Determined by the kind of dump mechanism and appropriate + * overall scheme + */ +struct dump_scheme_ops { + /* sets aside memory, inits data structures etc */ + int (*configure)(unsigned long devid); + /* releases resources */ + int (*unconfigure)(void); + + /* ordering of passes, invoking iterator */ + int (*sequencer)(void); + /* iterates over system data, selects and acts on data to dump */ + int (*iterator)(int, int (*)(unsigned long, unsigned long), + struct dump_data_filter *); + /* action when data is selected for dump */ + int (*save_data)(unsigned long, unsigned long); + /* action when data is to be excluded from dump */ + int (*skip_data)(unsigned long, unsigned long); + /* policies for space, multiple dump devices etc */ + int (*write_buffer)(void *, unsigned long); +}; + +struct dump_scheme { + /* the name serves as an anchor to locate the scheme after reboot */ + char name[32]; + struct dump_scheme_ops *ops; + struct list_head list; +}; + +/* Quiescing/Silence levels (controls IPI callback behaviour) */ +extern enum dump_silence_levels { + DUMP_SOFT_SPIN_CPUS = 1, + DUMP_HARD_SPIN_CPUS = 2, + DUMP_HALT_CPUS = 3, +} dump_silence_level; + +/* determined by the dump (file) format */ +struct dump_fmt_ops { + /* build header */ + int (*configure_header)(const char *, const struct pt_regs *); + int (*update_header)(void); /* update header and write it out */ + /* save curr context */ + void (*save_context)(int, const struct pt_regs *, + struct task_struct *); + /* typically called by the save_data action */ + /* add formatted data to the dump buffer */ + int (*add_data)(unsigned long, unsigned long); + int (*update_end_marker)(void); +}; + +struct dump_fmt { + unsigned long magic; + char name[32]; /* lcrash, crash, elf-core etc */ + struct dump_fmt_ops *ops; + struct list_head list; +}; + +/* + * Modules will be able add their own data capture schemes by + * registering their own dumpers. Typically they would use the + * primary dumper as a template and tune it with their routines. + * Still Todo. + */ + +/* The combined dumper profile (mechanism, scheme, dev, fmt) */ +struct dumper { + char name[32]; /* singlestage, overlay (stg1), passthru(stg2), pull */ + struct dump_scheme *scheme; + struct dump_fmt *fmt; + struct __dump_compress *compress; + struct dump_data_filter *filter; + struct dump_dev *dev; + /* state valid only for active dumper(s) - per instance */ + /* run time state/context */ + int curr_pass; + unsigned long count; + loff_t curr_offset; /* current logical offset into dump device */ + loff_t curr_loc; /* current memory location */ + void *curr_buf; /* current position in the dump buffer */ + void *dump_buf; /* starting addr of dump buffer */ + int header_dirty; /* whether the header needs to be written out */ + int header_len; + struct list_head dumper_list; /* links to other dumpers */ +}; + +/* Starting point to get to the current configured state */ +struct dump_config { + ulong level; + ulong flags; + struct dumper *dumper; + unsigned long dump_device; + unsigned long dump_addr; /* relevant only for in-memory dumps */ + struct list_head dump_dev_list; +}; + +extern struct dump_config dump_config; + +/* Used to save the dump config across a reboot for 2-stage dumps: + * + * Note: The scheme, format, compression and device type should be + * registered at bootup, for this config to be sharable across soft-boot. + * The function addresses could have changed and become invalid, and + * need to be set up again. + */ +struct dump_config_block { + u64 magic; /* for a quick sanity check after reboot */ + struct dump_memdev memdev; /* handle to dump stored in memory */ + struct dump_config config; + struct dumper dumper; + struct dump_scheme scheme; + struct dump_fmt fmt; + struct __dump_compress compress; + struct dump_data_filter filter_table[MAX_PASSES]; + struct dump_anydev dev[MAX_DEVS]; /* target dump device */ +}; + + +/* Wrappers that invoke the methods for the current (active) dumper */ + +/* Scheme operations */ + +static inline int dump_sequencer(void) +{ + return dump_config.dumper->scheme->ops->sequencer(); +} + +static inline int dump_iterator(int pass, int (*action)(unsigned long, + unsigned long), struct dump_data_filter *filter) +{ + return dump_config.dumper->scheme->ops->iterator(pass, action, filter); +} + +#define dump_save_data dump_config.dumper->scheme->ops->save_data +#define dump_skip_data dump_config.dumper->scheme->ops->skip_data + +static inline int dump_write_buffer(void *buf, unsigned long len) +{ + return dump_config.dumper->scheme->ops->write_buffer(buf, len); +} + +static inline int dump_configure(unsigned long devid) +{ + return dump_config.dumper->scheme->ops->configure(devid); +} + +static inline int dump_unconfigure(void) +{ + return dump_config.dumper->scheme->ops->unconfigure(); +} + +/* Format operations */ + +static inline int dump_configure_header(const char *panic_str, + const struct pt_regs *regs) +{ + return dump_config.dumper->fmt->ops->configure_header(panic_str, regs); +} + +static inline void dump_save_context(int cpu, const struct pt_regs *regs, + struct task_struct *tsk) +{ + dump_config.dumper->fmt->ops->save_context(cpu, regs, tsk); +} + +static inline int dump_save_this_cpu(const struct pt_regs *regs) +{ + int cpu = smp_processor_id(); + + dump_save_context(cpu, regs, current); + return 1; +} + +static inline int dump_update_header(void) +{ + return dump_config.dumper->fmt->ops->update_header(); +} + +static inline int dump_update_end_marker(void) +{ + return dump_config.dumper->fmt->ops->update_end_marker(); +} + +static inline int dump_add_data(unsigned long loc, unsigned long sz) +{ + return dump_config.dumper->fmt->ops->add_data(loc, sz); +} + +/* Compression operation */ +static inline int dump_compress_data(char *src, int slen, char *dst) +{ + return dump_config.dumper->compress->compress_func(src, slen, + dst, DUMP_DPC_PAGE_SIZE); +} + + +/* Prototypes of some default implementations of dump methods */ + +extern struct __dump_compress dump_none_compression; + +/* Default scheme methods (dump_scheme.c) */ + +extern int dump_generic_sequencer(void); +extern int dump_page_iterator(int pass, int (*action)(unsigned long, unsigned + long), struct dump_data_filter *filter); +extern int dump_generic_save_data(unsigned long loc, unsigned long sz); +extern int dump_generic_skip_data(unsigned long loc, unsigned long sz); +extern int dump_generic_write_buffer(void *buf, unsigned long len); +extern int dump_generic_configure(unsigned long); +extern int dump_generic_unconfigure(void); + +/* Default scheme template */ +extern struct dump_scheme dump_scheme_singlestage; + +/* Default dump format methods */ + +extern int dump_lcrash_configure_header(const char *panic_str, + const struct pt_regs *regs); +extern void dump_lcrash_save_context(int cpu, const struct pt_regs *regs, + struct task_struct *tsk); +extern int dump_generic_update_header(void); +extern int dump_lcrash_add_data(unsigned long loc, unsigned long sz); +extern int dump_lcrash_update_end_marker(void); + +/* Default format (lcrash) template */ +extern struct dump_fmt dump_fmt_lcrash; + +/* Default dump selection filter table */ + +/* + * Entries listed in order of importance and correspond to passes + * The last entry (with a level_mask of zero) typically reflects data that + * won't be dumped -- this may for example be used to identify data + * that will be skipped for certain so the corresponding memory areas can be + * utilized as scratch space. + */ +extern struct dump_data_filter dump_filter_table[]; + +/* Some pre-defined dumpers */ +extern struct dumper dumper_singlestage; +extern struct dumper dumper_stage1; +extern struct dumper dumper_stage2; + +/* These are temporary */ +#define DUMP_MASK_HEADER DUMP_LEVEL_HEADER +#define DUMP_MASK_KERN DUMP_LEVEL_KERN +#define DUMP_MASK_USED DUMP_LEVEL_USED +#define DUMP_MASK_UNUSED DUMP_LEVEL_ALL_RAM +#define DUMP_MASK_REST 0 /* dummy for now */ + +/* Helpers - move these to dump.h later ? */ + +int dump_generic_execute(const char *panic_str, const struct pt_regs *regs); +extern int dump_ll_write(void *buf, unsigned long len); +int dump_check_and_free_page(struct dump_memdev *dev, struct page *page); + +static inline void dumper_reset(void) +{ + dump_config.dumper->curr_buf = dump_config.dumper->dump_buf; + dump_config.dumper->curr_loc = 0; + dump_config.dumper->curr_offset = 0; + dump_config.dumper->count = 0; + dump_config.dumper->curr_pass = 0; +} + +/* + * May later be moulded to perform boot-time allocations so we can dump + * earlier during bootup + */ +static inline void *dump_alloc_mem(unsigned long size) +{ + return kmalloc(size, GFP_KERNEL); +} + +static inline void dump_free_mem(void *buf) +{ + struct page *page; + + /* ignore reserved pages (e.g. post soft boot stage) */ + if (buf && (page = virt_to_page(buf))) { + if (PageReserved(page)) + return; + } + + kfree(buf); +} + + +#endif /* _LINUX_DUMP_METHODS_H */ diff --git a/drivers/dump/dump_netdev.c b/drivers/dump/dump_netdev.c new file mode 100644 index 000000000..1feb6daf1 --- /dev/null +++ b/drivers/dump/dump_netdev.c @@ -0,0 +1,867 @@ +/* + * Implements the dump driver interface for saving a dump via network + * interface. + * + * Some of this code has been taken/adapted from Ingo Molnar's netconsole + * code. LKCD team expresses its thanks to Ingo. + * + * Started: June 2002 - Mohamed Abbas + * Adapted netconsole code to implement LKCD dump over the network. + * + * Nov 2002 - Bharata B. Rao + * Innumerable code cleanups, simplification and some fixes. + * Netdump configuration done by ioctl instead of using module parameters. + * + * Copyright (C) 2001 Ingo Molnar + * Copyright (C) 2002 International Business Machines Corp. + * + * This code is released under version 2 of the GNU GPL. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static int startup_handshake; +static int page_counter; +static struct net_device *dump_ndev; +static struct in_device *dump_in_dev; +static u16 source_port, target_port; +static u32 source_ip, target_ip; +static unsigned char daddr[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff} ; +static spinlock_t dump_skb_lock = SPIN_LOCK_UNLOCKED; +static int dump_nr_skbs; +static struct sk_buff *dump_skb; +static unsigned long flags_global; +static int netdump_in_progress; +static char device_name[IFNAMSIZ]; + +/* + * security depends on the trusted path between the netconsole + * server and netconsole client, since none of the packets are + * encrypted. The random magic number protects the protocol + * against spoofing. + */ +static u64 dump_magic; + +#define MAX_UDP_CHUNK 1460 +#define MAX_PRINT_CHUNK (MAX_UDP_CHUNK-HEADER_LEN) + +/* + * We maintain a small pool of fully-sized skbs, + * to make sure the message gets out even in + * extreme OOM situations. + */ +#define DUMP_MAX_SKBS 32 + +#define MAX_SKB_SIZE \ + (MAX_UDP_CHUNK + sizeof(struct udphdr) + \ + sizeof(struct iphdr) + sizeof(struct ethhdr)) + +static void +dump_refill_skbs(void) +{ + struct sk_buff *skb; + unsigned long flags; + + spin_lock_irqsave(&dump_skb_lock, flags); + while (dump_nr_skbs < DUMP_MAX_SKBS) { + skb = alloc_skb(MAX_SKB_SIZE, GFP_ATOMIC); + if (!skb) + break; + if (dump_skb) + skb->next = dump_skb; + else + skb->next = NULL; + dump_skb = skb; + dump_nr_skbs++; + } + spin_unlock_irqrestore(&dump_skb_lock, flags); +} + +static struct +sk_buff * dump_get_skb(void) +{ + struct sk_buff *skb; + unsigned long flags; + + spin_lock_irqsave(&dump_skb_lock, flags); + skb = dump_skb; + if (skb) { + dump_skb = skb->next; + skb->next = NULL; + dump_nr_skbs--; + } + spin_unlock_irqrestore(&dump_skb_lock, flags); + + return skb; +} + +/* + * Zap completed output skbs. + */ +static void +zap_completion_queue(void) +{ + int count; + unsigned long flags; + struct softnet_data *sd; + + count=0; + sd = &__get_cpu_var(softnet_data); + if (sd->completion_queue) { + struct sk_buff *clist; + + local_irq_save(flags); + clist = sd->completion_queue; + sd->completion_queue = NULL; + local_irq_restore(flags); + + while (clist != NULL) { + struct sk_buff *skb = clist; + clist = clist->next; + __kfree_skb(skb); + count++; + if (count > 10000) + printk("Error in sk list\n"); + } + } +} + +static void +dump_send_skb(struct net_device *dev, const char *msg, unsigned int msg_len, + reply_t *reply) +{ + int once = 1; + int total_len, eth_len, ip_len, udp_len, count = 0; + struct sk_buff *skb; + struct udphdr *udph; + struct iphdr *iph; + struct ethhdr *eth; + + udp_len = msg_len + HEADER_LEN + sizeof(*udph); + ip_len = eth_len = udp_len + sizeof(*iph); + total_len = eth_len + ETH_HLEN; + +repeat_loop: + zap_completion_queue(); + if (dump_nr_skbs < DUMP_MAX_SKBS) + dump_refill_skbs(); + + skb = alloc_skb(total_len, GFP_ATOMIC); + if (!skb) { + skb = dump_get_skb(); + if (!skb) { + count++; + if (once && (count == 1000000)) { + printk("possibly FATAL: out of netconsole " + "skbs!!! will keep retrying.\n"); + once = 0; + } + dev->poll_controller(dev); + goto repeat_loop; + } + } + + atomic_set(&skb->users, 1); + skb_reserve(skb, total_len - msg_len - HEADER_LEN); + skb->data[0] = NETCONSOLE_VERSION; + + put_unaligned(htonl(reply->nr), (u32 *) (skb->data + 1)); + put_unaligned(htonl(reply->code), (u32 *) (skb->data + 5)); + put_unaligned(htonl(reply->info), (u32 *) (skb->data + 9)); + + memcpy(skb->data + HEADER_LEN, msg, msg_len); + skb->len += msg_len + HEADER_LEN; + + udph = (struct udphdr *) skb_push(skb, sizeof(*udph)); + udph->source = source_port; + udph->dest = target_port; + udph->len = htons(udp_len); + udph->check = 0; + + iph = (struct iphdr *)skb_push(skb, sizeof(*iph)); + + iph->version = 4; + iph->ihl = 5; + iph->tos = 0; + iph->tot_len = htons(ip_len); + iph->id = 0; + iph->frag_off = 0; + iph->ttl = 64; + iph->protocol = IPPROTO_UDP; + iph->check = 0; + iph->saddr = source_ip; + iph->daddr = target_ip; + iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl); + + eth = (struct ethhdr *) skb_push(skb, ETH_HLEN); + + eth->h_proto = htons(ETH_P_IP); + memcpy(eth->h_source, dev->dev_addr, dev->addr_len); + memcpy(eth->h_dest, daddr, dev->addr_len); + + count=0; +repeat_poll: + spin_lock(&dev->xmit_lock); + dev->xmit_lock_owner = smp_processor_id(); + + count++; + + + if (netif_queue_stopped(dev)) { + dev->xmit_lock_owner = -1; + spin_unlock(&dev->xmit_lock); + + dev->poll_controller(dev); + zap_completion_queue(); + + + goto repeat_poll; + } + + dev->hard_start_xmit(skb, dev); + + dev->xmit_lock_owner = -1; + spin_unlock(&dev->xmit_lock); +} + +static unsigned short +udp_check(struct udphdr *uh, int len, unsigned long saddr, unsigned long daddr, + unsigned long base) +{ + return csum_tcpudp_magic(saddr, daddr, len, IPPROTO_UDP, base); +} + +static int +udp_checksum_init(struct sk_buff *skb, struct udphdr *uh, + unsigned short ulen, u32 saddr, u32 daddr) +{ + if (uh->check == 0) { + skb->ip_summed = CHECKSUM_UNNECESSARY; + } else if (skb->ip_summed == CHECKSUM_HW) { + skb->ip_summed = CHECKSUM_UNNECESSARY; + if (!udp_check(uh, ulen, saddr, daddr, skb->csum)) + return 0; + skb->ip_summed = CHECKSUM_NONE; + } + if (skb->ip_summed != CHECKSUM_UNNECESSARY) + skb->csum = csum_tcpudp_nofold(saddr, daddr, ulen, + IPPROTO_UDP, 0); + /* Probably, we should checksum udp header (it should be in cache + * in any case) and data in tiny packets (< rx copybreak). + */ + return 0; +} + +static __inline__ int +__udp_checksum_complete(struct sk_buff *skb) +{ + return (unsigned short)csum_fold(skb_checksum(skb, 0, skb->len, + skb->csum)); +} + +static __inline__ +int udp_checksum_complete(struct sk_buff *skb) +{ + return skb->ip_summed != CHECKSUM_UNNECESSARY && + __udp_checksum_complete(skb); +} + +int new_req = 0; +static req_t req; + +static int +dump_rx_hook(struct sk_buff *skb) +{ + int proto; + struct iphdr *iph; + struct udphdr *uh; + __u32 len, saddr, daddr, ulen; + req_t *__req; + + /* + * First check if were are dumping or doing startup handshake, if + * not quickly return. + */ + if (!netdump_in_progress) + return NET_RX_SUCCESS; + + if (skb->dev->type != ARPHRD_ETHER) + goto out; + + proto = ntohs(skb->mac.ethernet->h_proto); + if (proto != ETH_P_IP) + goto out; + + if (skb->pkt_type == PACKET_OTHERHOST) + goto out; + + if (skb_shared(skb)) + goto out; + + /* IP header correctness testing: */ + iph = (struct iphdr *)skb->data; + if (!pskb_may_pull(skb, sizeof(struct iphdr))) + goto out; + + if (iph->ihl < 5 || iph->version != 4) + goto out; + + if (!pskb_may_pull(skb, iph->ihl*4)) + goto out; + + if (ip_fast_csum((u8 *)iph, iph->ihl) != 0) + goto out; + + len = ntohs(iph->tot_len); + if (skb->len < len || len < iph->ihl*4) + goto out; + + saddr = iph->saddr; + daddr = iph->daddr; + if (iph->protocol != IPPROTO_UDP) + goto out; + + if (source_ip != daddr) + goto out; + + if (target_ip != saddr) + goto out; + + len -= iph->ihl*4; + uh = (struct udphdr *)(((char *)iph) + iph->ihl*4); + ulen = ntohs(uh->len); + + if (ulen != len || ulen < (sizeof(*uh) + sizeof(*__req))) + goto out; + + if (udp_checksum_init(skb, uh, ulen, saddr, daddr) < 0) + goto out; + + if (udp_checksum_complete(skb)) + goto out; + + if (source_port != uh->dest) + goto out; + + if (target_port != uh->source) + goto out; + + __req = (req_t *)(uh + 1); + if ((ntohl(__req->command) != COMM_GET_MAGIC) && + (ntohl(__req->command) != COMM_HELLO) && + (ntohl(__req->command) != COMM_START_WRITE_NETDUMP_ACK) && + (ntohl(__req->command) != COMM_START_NETDUMP_ACK) && + (memcmp(&__req->magic, &dump_magic, sizeof(dump_magic)) != 0)) + goto out; + + req.magic = ntohl(__req->magic); + req.command = ntohl(__req->command); + req.from = ntohl(__req->from); + req.to = ntohl(__req->to); + req.nr = ntohl(__req->nr); + new_req = 1; +out: + return NET_RX_DROP; +} + +static void +dump_send_mem(struct net_device *dev, req_t *req, const char* buff, size_t len) +{ + int i; + + int nr_chunks = len/1024; + reply_t reply; + + reply.nr = req->nr; + reply.info = 0; + + if ( nr_chunks <= 0) + nr_chunks = 1; + for (i = 0; i < nr_chunks; i++) { + unsigned int offset = i*1024; + reply.code = REPLY_MEM; + reply.info = offset; + dump_send_skb(dev, buff + offset, 1024, &reply); + } +} + +/* + * This function waits for the client to acknowledge the receipt + * of the netdump startup reply, with the possibility of packets + * getting lost. We resend the startup packet if no ACK is received, + * after a 1 second delay. + * + * (The client can test the success of the handshake via the HELLO + * command, and send ACKs until we enter netdump mode.) + */ +static int +dump_handshake(struct dump_dev *net_dev) +{ + char tmp[200]; + reply_t reply; + int i, j; + + if (startup_handshake) { + sprintf(tmp, "NETDUMP start, waiting for start-ACK.\n"); + reply.code = REPLY_START_NETDUMP; + reply.nr = 0; + reply.info = 0; + } else { + sprintf(tmp, "NETDUMP start, waiting for start-ACK.\n"); + reply.code = REPLY_START_WRITE_NETDUMP; + reply.nr = net_dev->curr_offset; + reply.info = net_dev->curr_offset; + } + + /* send 300 handshake packets before declaring failure */ + for (i = 0; i < 300; i++) { + dump_send_skb(dump_ndev, tmp, strlen(tmp), &reply); + + /* wait 1 sec */ + for (j = 0; j < 10000; j++) { + udelay(100); + dump_ndev->poll_controller(dump_ndev); + zap_completion_queue(); + if (new_req) + break; + } + + /* + * if there is no new request, try sending the handshaking + * packet again + */ + if (!new_req) + continue; + + /* + * check if the new request is of the expected type, + * if so, return, else try sending the handshaking + * packet again + */ + if (startup_handshake) { + if (req.command == COMM_HELLO || req.command == + COMM_START_NETDUMP_ACK) { + return 0; + } else { + new_req = 0; + continue; + } + } else { + if (req.command == COMM_SEND_MEM) { + return 0; + } else { + new_req = 0; + continue; + } + } + } + return -1; +} + +static ssize_t +do_netdump(struct dump_dev *net_dev, const char* buff, size_t len) +{ + reply_t reply; + char tmp[200]; + ssize_t ret = 0; + int repeatCounter, counter, total_loop; + + netdump_in_progress = 1; + + if (dump_handshake(net_dev) < 0) { + printk("network dump failed due to handshake failure\n"); + goto out; + } + + /* + * Ideally startup handshake should be done during dump configuration, + * i.e., in dump_net_open(). This will be done when I figure out + * the dependency between startup handshake, subsequent write and + * various commands wrt to net-server. + */ + if (startup_handshake) + startup_handshake = 0; + + counter = 0; + repeatCounter = 0; + total_loop = 0; + while (1) { + if (!new_req) { + dump_ndev->poll_controller(dump_ndev); + zap_completion_queue(); + } + if (!new_req) { + repeatCounter++; + + if (repeatCounter > 5) { + counter++; + if (counter > 10000) { + if (total_loop >= 100000) { + printk("Time OUT LEAVE NOW\n"); + goto out; + } else { + total_loop++; + printk("Try number %d out of " + "10 before Time Out\n", + total_loop); + } + } + mdelay(1); + repeatCounter = 0; + } + continue; + } + repeatCounter = 0; + counter = 0; + total_loop = 0; + new_req = 0; + switch (req.command) { + case COMM_NONE: + break; + + case COMM_SEND_MEM: + dump_send_mem(dump_ndev, &req, buff, len); + break; + + case COMM_EXIT: + case COMM_START_WRITE_NETDUMP_ACK: + ret = len; + goto out; + + case COMM_HELLO: + sprintf(tmp, "Hello, this is netdump version " + "0.%02d\n", NETCONSOLE_VERSION); + reply.code = REPLY_HELLO; + reply.nr = req.nr; + reply.info = net_dev->curr_offset; + dump_send_skb(dump_ndev, tmp, strlen(tmp), &reply); + break; + + case COMM_GET_PAGE_SIZE: + sprintf(tmp, "PAGE_SIZE: %ld\n", PAGE_SIZE); + reply.code = REPLY_PAGE_SIZE; + reply.nr = req.nr; + reply.info = PAGE_SIZE; + dump_send_skb(dump_ndev, tmp, strlen(tmp), &reply); + break; + + case COMM_GET_NR_PAGES: + reply.code = REPLY_NR_PAGES; + reply.nr = req.nr; + reply.info = num_physpages; + reply.info = page_counter; + sprintf(tmp, "Number of pages: %ld\n", num_physpages); + dump_send_skb(dump_ndev, tmp, strlen(tmp), &reply); + break; + + case COMM_GET_MAGIC: + reply.code = REPLY_MAGIC; + reply.nr = req.nr; + reply.info = NETCONSOLE_VERSION; + dump_send_skb(dump_ndev, (char *)&dump_magic, + sizeof(dump_magic), &reply); + break; + + default: + reply.code = REPLY_ERROR; + reply.nr = req.nr; + reply.info = req.command; + sprintf(tmp, "Got unknown command code %d!\n", + req.command); + dump_send_skb(dump_ndev, tmp, strlen(tmp), &reply); + break; + } + } +out: + netdump_in_progress = 0; + return ret; +} + +static int +dump_validate_config(void) +{ + source_ip = dump_in_dev->ifa_list->ifa_local; + if (!source_ip) { + printk("network device %s has no local address, " + "aborting.\n", device_name); + return -1; + } + +#define IP(x) ((unsigned char *)&source_ip)[x] + printk("Source %d.%d.%d.%d", IP(0), IP(1), IP(2), IP(3)); +#undef IP + + if (!source_port) { + printk("source_port parameter not specified, aborting.\n"); + return -1; + } + printk(":%i\n", source_port); + source_port = htons(source_port); + + if (!target_ip) { + printk("target_ip parameter not specified, aborting.\n"); + return -1; + } + +#define IP(x) ((unsigned char *)&target_ip)[x] + printk("Target %d.%d.%d.%d", IP(0), IP(1), IP(2), IP(3)); +#undef IP + + if (!target_port) { + printk("target_port parameter not specified, aborting.\n"); + return -1; + } + printk(":%i\n", target_port); + target_port = htons(target_port); + + printk("Target Ethernet Address %02x:%02x:%02x:%02x:%02x:%02x", + daddr[0], daddr[1], daddr[2], daddr[3], daddr[4], daddr[5]); + + if ((daddr[0] & daddr[1] & daddr[2] & daddr[3] & daddr[4] & + daddr[5]) == 255) + printk("(Broadcast)"); + printk("\n"); + return 0; +} + +/* + * Prepares the dump device so we can take a dump later. + * Validates the netdump configuration parameters. + * + * TODO: Network connectivity check should be done here. + */ +static int +dump_net_open(struct dump_dev *net_dev, unsigned long arg) +{ + int retval = 0; + + /* get the interface name */ + if (copy_from_user(device_name, (void *)arg, IFNAMSIZ)) + return -EFAULT; + + if (!(dump_ndev = dev_get_by_name(device_name))) { + printk("network device %s does not exist, aborting.\n", + device_name); + return -ENODEV; + } + + if (!dump_ndev->poll_controller) { + printk("network device %s does not implement polling yet, " + "aborting.\n", device_name); + retval = -1; /* return proper error */ + goto err1; + } + + if (!(dump_in_dev = in_dev_get(dump_ndev))) { + printk("network device %s is not an IP protocol device, " + "aborting.\n", device_name); + retval = -EINVAL; + goto err1; + } + + if ((retval = dump_validate_config()) < 0) + goto err2; + + net_dev->curr_offset = 0; + printk("Network device %s successfully configured for dumping\n", + device_name); + return retval; +err2: + in_dev_put(dump_in_dev); +err1: + dev_put(dump_ndev); + return retval; +} + +/* + * Close the dump device and release associated resources + * Invoked when unconfiguring the dump device. + */ +static int +dump_net_release(struct dump_dev *net_dev) +{ + if (dump_in_dev) + in_dev_put(dump_in_dev); + if (dump_ndev) + dev_put(dump_ndev); + return 0; +} + +/* + * Prepare the dump device for use (silence any ongoing activity + * and quiesce state) when the system crashes. + */ +static int +dump_net_silence(struct dump_dev *net_dev) +{ + netpoll_set_trap(1); + local_irq_save(flags_global); + dump_ndev->rx_hook = dump_rx_hook; + startup_handshake = 1; + net_dev->curr_offset = 0; + printk("Dumping to network device %s on CPU %d ...\n", device_name, + smp_processor_id()); + return 0; +} + +/* + * Invoked when dumping is done. This is the time to put things back + * (i.e. undo the effects of dump_block_silence) so the device is + * available for normal use. + */ +static int +dump_net_resume(struct dump_dev *net_dev) +{ + int indx; + reply_t reply; + char tmp[200]; + + if (!dump_ndev) + return (0); + + sprintf(tmp, "NETDUMP end.\n"); + for( indx = 0; indx < 6; indx++) { + reply.code = REPLY_END_NETDUMP; + reply.nr = 0; + reply.info = 0; + dump_send_skb(dump_ndev, tmp, strlen(tmp), &reply); + } + printk("NETDUMP END!\n"); + local_irq_restore(flags_global); + netpoll_set_trap(0); + dump_ndev->rx_hook = NULL; + startup_handshake = 0; + return 0; +} + +/* + * Seek to the specified offset in the dump device. + * Makes sure this is a valid offset, otherwise returns an error. + */ +static int +dump_net_seek(struct dump_dev *net_dev, loff_t off) +{ + /* + * For now using DUMP_HEADER_OFFSET as hard coded value, + * See dump_block_seekin dump_blockdev.c to know how to + * do this properly. + */ + net_dev->curr_offset = off; + return 0; +} + +/* + * + */ +static int +dump_net_write(struct dump_dev *net_dev, void *buf, unsigned long len) +{ + int cnt, i, off; + ssize_t ret; + + cnt = len/ PAGE_SIZE; + + for (i = 0; i < cnt; i++) { + off = i* PAGE_SIZE; + ret = do_netdump(net_dev, buf+off, PAGE_SIZE); + if (ret <= 0) + return -1; + net_dev->curr_offset = net_dev->curr_offset + PAGE_SIZE; + } + return len; +} + +/* + * check if the last dump i/o is over and ready for next request + */ +static int +dump_net_ready(struct dump_dev *net_dev, void *buf) +{ + return 0; +} + +/* + * ioctl function used for configuring network dump + */ +static int +dump_net_ioctl(struct dump_dev *net_dev, unsigned int cmd, unsigned long arg) +{ + switch (cmd) { + case DIOSTARGETIP: + target_ip = arg; + break; + case DIOSTARGETPORT: + target_port = (u16)arg; + break; + case DIOSSOURCEPORT: + source_port = (u16)arg; + break; + case DIOSETHADDR: + return copy_from_user(daddr, (void *)arg, 6); + break; + case DIOGTARGETIP: + case DIOGTARGETPORT: + case DIOGSOURCEPORT: + case DIOGETHADDR: + break; + default: + return -EINVAL; + } + return 0; +} + +struct dump_dev_ops dump_netdev_ops = { + .open = dump_net_open, + .release = dump_net_release, + .silence = dump_net_silence, + .resume = dump_net_resume, + .seek = dump_net_seek, + .write = dump_net_write, + /* .read not implemented */ + .ready = dump_net_ready, + .ioctl = dump_net_ioctl +}; + +static struct dump_dev default_dump_netdev = { + .type_name = "networkdev", + .ops = &dump_netdev_ops, + .curr_offset = 0 +}; + +static int __init +dump_netdev_init(void) +{ + default_dump_netdev.curr_offset = 0; + + if (dump_register_device(&default_dump_netdev) < 0) { + printk("network dump device driver registration failed\n"); + return -1; + } + printk("network device driver for LKCD registered\n"); + + get_random_bytes(&dump_magic, sizeof(dump_magic)); + return 0; +} + +static void __exit +dump_netdev_cleanup(void) +{ + dump_unregister_device(&default_dump_netdev); +} + +MODULE_AUTHOR("LKCD Development Team "); +MODULE_DESCRIPTION("Network Dump Driver for Linux Kernel Crash Dump (LKCD)"); +MODULE_LICENSE("GPL"); + +module_init(dump_netdev_init); +module_exit(dump_netdev_cleanup); diff --git a/drivers/dump/dump_overlay.c b/drivers/dump/dump_overlay.c new file mode 100644 index 000000000..8e10b7827 --- /dev/null +++ b/drivers/dump/dump_overlay.c @@ -0,0 +1,884 @@ +/* + * Two-stage soft-boot based dump scheme methods (memory overlay + * with post soft-boot writeout) + * + * Started: Oct 2002 - Suparna Bhattacharya + * + * This approach of saving the dump in memory and writing it + * out after a softboot without clearing memory is derived from the + * Mission Critical Linux dump implementation. Credits and a big + * thanks for letting the lkcd project make use of the excellent + * piece of work and also for helping with clarifications and + * tips along the way are due to: + * Dave Winchell (primary author of mcore) + * and also to + * Jeff Moyer + * Josh Huber + * + * For those familiar with the mcore implementation, the key + * differences/extensions here are in allowing entire memory to be + * saved (in compressed form) through a careful ordering scheme + * on both the way down as well on the way up after boot, the latter + * for supporting the LKCD notion of passes in which most critical + * data is the first to be saved to the dump device. Also the post + * boot writeout happens from within the kernel rather than driven + * from userspace. + * + * The sequence is orchestrated through the abstraction of "dumpers", + * one for the first stage which then sets up the dumper for the next + * stage, providing for a smooth and flexible reuse of the singlestage + * dump scheme methods and a handle to pass dump device configuration + * information across the soft boot. + * + * Copyright (C) 2002 International Business Machines Corp. + * + * This code is released under version 2 of the GNU GPL. + */ + +/* + * Disruptive dumping using the second kernel soft-boot option + * for issuing dump i/o operates in 2 stages: + * + * (1) - Saves the (compressed & formatted) dump in memory using a + * carefully ordered overlay scheme designed to capture the + * entire physical memory or selective portions depending on + * dump config settings, + * - Registers the stage 2 dumper and + * - Issues a soft reboot w/o clearing memory. + * + * The overlay scheme starts with a small bootstrap free area + * and follows a reverse ordering of passes wherein it + * compresses and saves data starting with the least critical + * areas first, thus freeing up the corresponding pages to + * serve as destination for subsequent data to be saved, and + * so on. With a good compression ratio, this makes it feasible + * to capture an entire physical memory dump without significantly + * reducing memory available during regular operation. + * + * (2) Post soft-reboot, runs through the saved memory dump and + * writes it out to disk, this time around, taking care to + * save the more critical data first (i.e. pages which figure + * in early passes for a regular dump). Finally issues a + * clean reboot. + * + * Since the data was saved in memory after selection/filtering + * and formatted as per the chosen output dump format, at this + * stage the filter and format actions are just dummy (or + * passthrough) actions, except for influence on ordering of + * passes. + */ + +#include +#include +#include +#include +#include +#ifdef CONFIG_KEXEC +#include +#include +#include +#endif +#include "dump_methods.h" + +extern struct list_head dumper_list_head; +extern struct dump_memdev *dump_memdev; +extern struct dumper dumper_stage2; +struct dump_config_block *dump_saved_config = NULL; +extern struct dump_blockdev *dump_blockdev; +static struct dump_memdev *saved_dump_memdev = NULL; +static struct dumper *saved_dumper = NULL; + +#ifdef CONFIG_KEXEC +extern int panic_timeout; +#endif + +/* For testing +extern void dump_display_map(struct dump_memdev *); +*/ + +struct dumper *dumper_by_name(char *name) +{ +#ifdef LATER + struct dumper *dumper; + list_for_each_entry(dumper, &dumper_list_head, dumper_list) + if (!strncmp(dumper->name, name, 32)) + return dumper; + + /* not found */ + return NULL; +#endif + /* Temporary proof of concept */ + if (!strncmp(dumper_stage2.name, name, 32)) + return &dumper_stage2; + else + return NULL; +} + +#ifdef CONFIG_CRASH_DUMP_SOFTBOOT +extern void dump_early_reserve_map(struct dump_memdev *); + +void crashdump_reserve(void) +{ + extern unsigned long crashdump_addr; + + if (crashdump_addr == 0xdeadbeef) + return; + + /* reserve dump config and saved dump pages */ + dump_saved_config = (struct dump_config_block *)crashdump_addr; + /* magic verification */ + if (dump_saved_config->magic != DUMP_MAGIC_LIVE) { + printk("Invalid dump magic. Ignoring dump\n"); + dump_saved_config = NULL; + return; + } + + printk("Dump may be available from previous boot\n"); + + reserve_bootmem(virt_to_phys((void *)crashdump_addr), + PAGE_ALIGN(sizeof(struct dump_config_block))); + dump_early_reserve_map(&dump_saved_config->memdev); + +} +#endif + +/* + * Loads the dump configuration from a memory block saved across soft-boot + * The ops vectors need fixing up as the corresp. routines may have + * relocated in the new soft-booted kernel. + */ +int dump_load_config(struct dump_config_block *config) +{ + struct dumper *dumper; + struct dump_data_filter *filter_table, *filter; + struct dump_dev *dev; + int i; + + if (config->magic != DUMP_MAGIC_LIVE) + return -ENOENT; /* not a valid config */ + + /* initialize generic config data */ + memcpy(&dump_config, &config->config, sizeof(dump_config)); + + /* initialize dumper state */ + if (!(dumper = dumper_by_name(config->dumper.name))) { + printk("dumper name mismatch\n"); + return -ENOENT; /* dumper mismatch */ + } + + /* verify and fixup schema */ + if (strncmp(dumper->scheme->name, config->scheme.name, 32)) { + printk("dumper scheme mismatch\n"); + return -ENOENT; /* mismatch */ + } + config->scheme.ops = dumper->scheme->ops; + config->dumper.scheme = &config->scheme; + + /* verify and fixup filter operations */ + filter_table = dumper->filter; + for (i = 0, filter = config->filter_table; + ((i < MAX_PASSES) && filter_table[i].selector); + i++, filter++) { + if (strncmp(filter_table[i].name, filter->name, 32)) { + printk("dump filter mismatch\n"); + return -ENOENT; /* filter name mismatch */ + } + filter->selector = filter_table[i].selector; + } + config->dumper.filter = config->filter_table; + + /* fixup format */ + if (strncmp(dumper->fmt->name, config->fmt.name, 32)) { + printk("dump format mismatch\n"); + return -ENOENT; /* mismatch */ + } + config->fmt.ops = dumper->fmt->ops; + config->dumper.fmt = &config->fmt; + + /* fixup target device */ + dev = (struct dump_dev *)(&config->dev[0]); + if (dumper->dev == NULL) { + pr_debug("Vanilla dumper - assume default\n"); + if (dump_dev == NULL) + return -ENODEV; + dumper->dev = dump_dev; + } + + if (strncmp(dumper->dev->type_name, dev->type_name, 32)) { + printk("dump dev type mismatch %s instead of %s\n", + dev->type_name, dumper->dev->type_name); + return -ENOENT; /* mismatch */ + } + dev->ops = dumper->dev->ops; + config->dumper.dev = dev; + + /* fixup memory device containing saved dump pages */ + /* assume statically init'ed dump_memdev */ + config->memdev.ddev.ops = dump_memdev->ddev.ops; + /* switch to memdev from prev boot */ + saved_dump_memdev = dump_memdev; /* remember current */ + dump_memdev = &config->memdev; + + /* Make this the current primary dumper */ + dump_config.dumper = &config->dumper; + + return 0; +} + +/* Saves the dump configuration in a memory block for use across a soft-boot */ +int dump_save_config(struct dump_config_block *config) +{ + printk("saving dump config settings\n"); + + /* dump config settings */ + memcpy(&config->config, &dump_config, sizeof(dump_config)); + + /* dumper state */ + memcpy(&config->dumper, dump_config.dumper, sizeof(struct dumper)); + memcpy(&config->scheme, dump_config.dumper->scheme, + sizeof(struct dump_scheme)); + memcpy(&config->fmt, dump_config.dumper->fmt, sizeof(struct dump_fmt)); + memcpy(&config->dev[0], dump_config.dumper->dev, + sizeof(struct dump_anydev)); + memcpy(&config->filter_table, dump_config.dumper->filter, + sizeof(struct dump_data_filter)*MAX_PASSES); + + /* handle to saved mem pages */ + memcpy(&config->memdev, dump_memdev, sizeof(struct dump_memdev)); + + config->magic = DUMP_MAGIC_LIVE; + + return 0; +} + +int dump_init_stage2(struct dump_config_block *saved_config) +{ + int err = 0; + + pr_debug("dump_init_stage2\n"); + /* Check if dump from previous boot exists */ + if (saved_config) { + printk("loading dumper from previous boot \n"); + /* load and configure dumper from previous boot */ + if ((err = dump_load_config(saved_config))) + return err; + + if (!dump_oncpu) { + if ((err = dump_configure(dump_config.dump_device))) { + printk("Stage 2 dump configure failed\n"); + return err; + } + } + + dumper_reset(); + dump_dev = dump_config.dumper->dev; + /* write out the dump */ + err = dump_generic_execute(NULL, NULL); + + dump_saved_config = NULL; + + if (!dump_oncpu) { + dump_unconfigure(); + } + + return err; + + } else { + /* no dump to write out */ + printk("no dumper from previous boot \n"); + return 0; + } +} + +extern void dump_mem_markpages(struct dump_memdev *); + +int dump_switchover_stage(void) +{ + int ret = 0; + + /* trigger stage 2 rightaway - in real life would be after soft-boot */ + /* dump_saved_config would be a boot param */ + saved_dump_memdev = dump_memdev; + saved_dumper = dump_config.dumper; + ret = dump_init_stage2(dump_saved_config); + dump_memdev = saved_dump_memdev; + dump_config.dumper = saved_dumper; + return ret; +} + +int dump_activate_softboot(void) +{ + int err = 0; +#ifdef CONFIG_KEXEC + int num_cpus_online = 0; + struct kimage *image; +#endif + + /* temporary - switchover to writeout previously saved dump */ +#ifndef CONFIG_KEXEC + err = dump_switchover_stage(); /* non-disruptive case */ + if (dump_oncpu) + dump_config.dumper = &dumper_stage1; /* set things back */ + + return err; +#else + + dump_silence_level = DUMP_HALT_CPUS; + /* wait till we become the only cpu */ + /* maybe by checking for online cpus ? */ + + while((num_cpus_online = num_online_cpus()) > 1); + + /* now call into kexec */ + + image = xchg(&kexec_image, 0); + if (image) { + mdelay(panic_timeout*1000); + machine_kexec(image); + } + + + /* TBD/Fixme: + * * should we call reboot notifiers ? inappropriate for panic ? + * * what about device_shutdown() ? + * * is explicit bus master disabling needed or can we do that + * * through driverfs ? + * */ + return 0; +#endif +} + +/* --- DUMP SCHEME ROUTINES --- */ + +static inline int dump_buf_pending(struct dumper *dumper) +{ + return (dumper->curr_buf - dumper->dump_buf); +} + +/* Invoked during stage 1 of soft-reboot based dumping */ +int dump_overlay_sequencer(void) +{ + struct dump_data_filter *filter = dump_config.dumper->filter; + struct dump_data_filter *filter2 = dumper_stage2.filter; + int pass = 0, err = 0, save = 0; + int (*action)(unsigned long, unsigned long); + + /* Make sure gzip compression is being used */ + if (dump_config.dumper->compress->compress_type != DUMP_COMPRESS_GZIP) { + printk(" Please set GZIP compression \n"); + return -EINVAL; + } + + /* start filling in dump data right after the header */ + dump_config.dumper->curr_offset = + PAGE_ALIGN(dump_config.dumper->header_len); + + /* Locate the last pass */ + for (;filter->selector; filter++, pass++); + + /* + * Start from the end backwards: overlay involves a reverse + * ordering of passes, since less critical pages are more + * likely to be reusable as scratch space once we are through + * with them. + */ + for (--pass, --filter; pass >= 0; pass--, filter--) + { + /* Assumes passes are exclusive (even across dumpers) */ + /* Requires care when coding the selection functions */ + if ((save = filter->level_mask & dump_config.level)) + action = dump_save_data; + else + action = dump_skip_data; + + /* Remember the offset where this pass started */ + /* The second stage dumper would use this */ + if (dump_buf_pending(dump_config.dumper) & (PAGE_SIZE - 1)) { + pr_debug("Starting pass %d with pending data\n", pass); + pr_debug("filling dummy data to page-align it\n"); + dump_config.dumper->curr_buf = (void *)PAGE_ALIGN( + (unsigned long)dump_config.dumper->curr_buf); + } + + filter2[pass].start[0] = dump_config.dumper->curr_offset + + dump_buf_pending(dump_config.dumper); + + err = dump_iterator(pass, action, filter); + + filter2[pass].end[0] = dump_config.dumper->curr_offset + + dump_buf_pending(dump_config.dumper); + filter2[pass].num_mbanks = 1; + + if (err < 0) { + printk("dump_overlay_seq: failure %d in pass %d\n", + err, pass); + break; + } + printk("\n %d overlay pages %s of %d each in pass %d\n", + err, save ? "saved" : "skipped", DUMP_PAGE_SIZE, pass); + } + + return err; +} + +/* from dump_memdev.c */ +extern struct page *dump_mem_lookup(struct dump_memdev *dev, unsigned long loc); +extern struct page *dump_mem_next_page(struct dump_memdev *dev); + +static inline struct page *dump_get_saved_page(loff_t loc) +{ + return (dump_mem_lookup(dump_memdev, loc >> PAGE_SHIFT)); +} + +static inline struct page *dump_next_saved_page(void) +{ + return (dump_mem_next_page(dump_memdev)); +} + +/* + * Iterates over list of saved dump pages. Invoked during second stage of + * soft boot dumping + * + * Observation: If additional selection is desired at this stage then + * a different iterator could be written which would advance + * to the next page header everytime instead of blindly picking up + * the data. In such a case loc would be interpreted differently. + * At this moment however a blind pass seems sufficient, cleaner and + * faster. + */ +int dump_saved_data_iterator(int pass, int (*action)(unsigned long, + unsigned long), struct dump_data_filter *filter) +{ + loff_t loc, end; + struct page *page; + unsigned long count = 0; + int i, err = 0; + unsigned long sz; + + for (i = 0; i < filter->num_mbanks; i++) { + loc = filter->start[i]; + end = filter->end[i]; + printk("pass %d, start off 0x%llx end offset 0x%llx\n", pass, + loc, end); + + /* loc will get treated as logical offset into stage 1 */ + page = dump_get_saved_page(loc); + + for (; loc < end; loc += PAGE_SIZE) { + dump_config.dumper->curr_loc = loc; + if (!page) { + printk("no more saved data for pass %d\n", + pass); + break; + } + sz = (loc + PAGE_SIZE > end) ? end - loc : PAGE_SIZE; + + if (page && filter->selector(pass, (unsigned long)page, + PAGE_SIZE)) { + pr_debug("mem offset 0x%llx\n", loc); + if ((err = action((unsigned long)page, sz))) + break; + else + count++; + /* clear the contents of page */ + /* fixme: consider using KM_DUMP instead */ + clear_highpage(page); + + } + page = dump_next_saved_page(); + } + } + + return err ? err : count; +} + +static inline int dump_overlay_pages_done(struct page *page, int nr) +{ + int ret=0; + + for (; nr ; page++, nr--) { + if (dump_check_and_free_page(dump_memdev, page)) + ret++; + } + return ret; +} + +int dump_overlay_save_data(unsigned long loc, unsigned long len) +{ + int err = 0; + struct page *page = (struct page *)loc; + static unsigned long cnt = 0; + + if ((err = dump_generic_save_data(loc, len))) + return err; + + if (dump_overlay_pages_done(page, len >> PAGE_SHIFT)) { + cnt++; + if (!(cnt & 0x7f)) + pr_debug("released page 0x%lx\n", page_to_pfn(page)); + } + + return err; +} + + +int dump_overlay_skip_data(unsigned long loc, unsigned long len) +{ + struct page *page = (struct page *)loc; + + dump_overlay_pages_done(page, len >> PAGE_SHIFT); + return 0; +} + +int dump_overlay_resume(void) +{ + int err = 0; + + /* + * switch to stage 2 dumper, save dump_config_block + * and then trigger a soft-boot + */ + dumper_stage2.header_len = dump_config.dumper->header_len; + dump_config.dumper = &dumper_stage2; + if ((err = dump_save_config(dump_saved_config))) + return err; + + dump_dev = dump_config.dumper->dev; + +#ifdef CONFIG_KEXEC + /* If we are doing a disruptive dump, activate softboot now */ + if((panic_timeout > 0) && (!(dump_config.flags & DUMP_FLAGS_NONDISRUPT))) + err = dump_activate_softboot(); +#endif + + return err; + err = dump_switchover_stage(); /* plugs into soft boot mechanism */ + dump_config.dumper = &dumper_stage1; /* set things back */ + return err; +} + +int dump_overlay_configure(unsigned long devid) +{ + struct dump_dev *dev; + struct dump_config_block *saved_config = dump_saved_config; + int err = 0; + + /* If there is a previously saved dump, write it out first */ + if (saved_config) { + printk("Processing old dump pending writeout\n"); + err = dump_switchover_stage(); + if (err) { + printk("failed to writeout saved dump\n"); + return err; + } + dump_free_mem(saved_config); /* testing only: not after boot */ + } + + dev = dumper_stage2.dev = dump_config.dumper->dev; + /* From here on the intermediate dump target is memory-only */ + dump_dev = dump_config.dumper->dev = &dump_memdev->ddev; + if ((err = dump_generic_configure(0))) { + printk("dump generic configure failed: err %d\n", err); + return err; + } + /* temporary */ + dumper_stage2.dump_buf = dump_config.dumper->dump_buf; + + /* Sanity check on the actual target dump device */ + if (!dev || (err = dev->ops->open(dev, devid))) { + return err; + } + /* TBD: should we release the target if this is soft-boot only ? */ + + /* alloc a dump config block area to save across reboot */ + if (!(dump_saved_config = dump_alloc_mem(sizeof(struct + dump_config_block)))) { + printk("dump config block alloc failed\n"); + /* undo configure */ + dump_generic_unconfigure(); + return -ENOMEM; + } + dump_config.dump_addr = (unsigned long)dump_saved_config; + printk("Dump config block of size %d set up at 0x%lx\n", + sizeof(*dump_saved_config), (unsigned long)dump_saved_config); + return 0; +} + +int dump_overlay_unconfigure(void) +{ + struct dump_dev *dev = dumper_stage2.dev; + int err = 0; + + pr_debug("dump_overlay_unconfigure\n"); + /* Close the secondary device */ + dev->ops->release(dev); + pr_debug("released secondary device\n"); + + err = dump_generic_unconfigure(); + pr_debug("Unconfigured generic portions\n"); + dump_free_mem(dump_saved_config); + dump_saved_config = NULL; + pr_debug("Freed saved config block\n"); + dump_dev = dump_config.dumper->dev = dumper_stage2.dev; + + printk("Unconfigured overlay dumper\n"); + return err; +} + +int dump_staged_unconfigure(void) +{ + int err = 0; + struct dump_config_block *saved_config = dump_saved_config; + struct dump_dev *dev; + + pr_debug("dump_staged_unconfigure\n"); + err = dump_generic_unconfigure(); + + /* now check if there is a saved dump waiting to be written out */ + if (saved_config) { + printk("Processing saved dump pending writeout\n"); + if ((err = dump_switchover_stage())) { + printk("Error in commiting saved dump at 0x%lx\n", + (unsigned long)saved_config); + printk("Old dump may hog memory\n"); + } else { + dump_free_mem(saved_config); + pr_debug("Freed saved config block\n"); + } + dump_saved_config = NULL; + } else { + dev = &dump_memdev->ddev; + dev->ops->release(dev); + } + printk("Unconfigured second stage dumper\n"); + + return 0; +} + +/* ----- PASSTHRU FILTER ROUTINE --------- */ + +/* transparent - passes everything through */ +int dump_passthru_filter(int pass, unsigned long loc, unsigned long sz) +{ + return 1; +} + +/* ----- PASSTRU FORMAT ROUTINES ---- */ + + +int dump_passthru_configure_header(const char *panic_str, const struct pt_regs *regs) +{ + dump_config.dumper->header_dirty++; + return 0; +} + +/* Copies bytes of data from page(s) to the specified buffer */ +int dump_copy_pages(void *buf, struct page *page, unsigned long sz) +{ + unsigned long len = 0, bytes; + void *addr; + + while (len < sz) { + addr = kmap_atomic(page, KM_DUMP); + bytes = (sz > len + PAGE_SIZE) ? PAGE_SIZE : sz - len; + memcpy(buf, addr, bytes); + kunmap_atomic(addr, KM_DUMP); + buf += bytes; + len += bytes; + page++; + } + /* memset(dump_config.dumper->curr_buf, 0x57, len); temporary */ + + return sz - len; +} + +int dump_passthru_update_header(void) +{ + long len = dump_config.dumper->header_len; + struct page *page; + void *buf = dump_config.dumper->dump_buf; + int err = 0; + + if (!dump_config.dumper->header_dirty) + return 0; + + pr_debug("Copying header of size %ld bytes from memory\n", len); + if (len > DUMP_BUFFER_SIZE) + return -E2BIG; + + page = dump_mem_lookup(dump_memdev, 0); + for (; (len > 0) && page; buf += PAGE_SIZE, len -= PAGE_SIZE) { + if ((err = dump_copy_pages(buf, page, PAGE_SIZE))) + return err; + page = dump_mem_next_page(dump_memdev); + } + if (len > 0) { + printk("Incomplete header saved in mem\n"); + return -ENOENT; + } + + if ((err = dump_dev_seek(0))) { + printk("Unable to seek to dump header offset\n"); + return err; + } + err = dump_ll_write(dump_config.dumper->dump_buf, + buf - dump_config.dumper->dump_buf); + if (err < dump_config.dumper->header_len) + return (err < 0) ? err : -ENOSPC; + + dump_config.dumper->header_dirty = 0; + return 0; +} + +static loff_t next_dph_offset = 0; + +static int dph_valid(struct __dump_page *dph) +{ + if ((dph->dp_address & (PAGE_SIZE - 1)) || (dph->dp_flags + > DUMP_DH_COMPRESSED) || (!dph->dp_flags) || + (dph->dp_size > PAGE_SIZE)) { + printk("dp->address = 0x%llx, dp->size = 0x%x, dp->flag = 0x%x\n", + dph->dp_address, dph->dp_size, dph->dp_flags); + return 0; + } + return 1; +} + +int dump_verify_lcrash_data(void *buf, unsigned long sz) +{ + struct __dump_page *dph; + + /* sanity check for page headers */ + while (next_dph_offset + sizeof(*dph) < sz) { + dph = (struct __dump_page *)(buf + next_dph_offset); + if (!dph_valid(dph)) { + printk("Invalid page hdr at offset 0x%llx\n", + next_dph_offset); + return -EINVAL; + } + next_dph_offset += dph->dp_size + sizeof(*dph); + } + + next_dph_offset -= sz; + return 0; +} + +/* + * TBD/Later: Consider avoiding the copy by using a scatter/gather + * vector representation for the dump buffer + */ +int dump_passthru_add_data(unsigned long loc, unsigned long sz) +{ + struct page *page = (struct page *)loc; + void *buf = dump_config.dumper->curr_buf; + int err = 0; + + if ((err = dump_copy_pages(buf, page, sz))) { + printk("dump_copy_pages failed"); + return err; + } + + if ((err = dump_verify_lcrash_data(buf, sz))) { + printk("dump_verify_lcrash_data failed\n"); + printk("Invalid data for pfn 0x%lx\n", page_to_pfn(page)); + printk("Page flags 0x%lx\n", page->flags); + printk("Page count 0x%x\n", atomic_read(&page->count)); + return err; + } + + dump_config.dumper->curr_buf = buf + sz; + + return 0; +} + + +/* Stage 1 dumper: Saves compressed dump in memory and soft-boots system */ + +/* Scheme to overlay saved data in memory for writeout after a soft-boot */ +struct dump_scheme_ops dump_scheme_overlay_ops = { + .configure = dump_overlay_configure, + .unconfigure = dump_overlay_unconfigure, + .sequencer = dump_overlay_sequencer, + .iterator = dump_page_iterator, + .save_data = dump_overlay_save_data, + .skip_data = dump_overlay_skip_data, + .write_buffer = dump_generic_write_buffer +}; + +struct dump_scheme dump_scheme_overlay = { + .name = "overlay", + .ops = &dump_scheme_overlay_ops +}; + + +/* Stage 1 must use a good compression scheme - default to gzip */ +extern struct __dump_compress dump_gzip_compression; + +struct dumper dumper_stage1 = { + .name = "stage1", + .scheme = &dump_scheme_overlay, + .fmt = &dump_fmt_lcrash, + .compress = &dump_none_compression, /* needs to be gzip */ + .filter = dump_filter_table, + .dev = NULL, +}; + +/* Stage 2 dumper: Activated after softboot to write out saved dump to device */ + +/* Formatter that transfers data as is (transparent) w/o further conversion */ +struct dump_fmt_ops dump_fmt_passthru_ops = { + .configure_header = dump_passthru_configure_header, + .update_header = dump_passthru_update_header, + .save_context = NULL, /* unused */ + .add_data = dump_passthru_add_data, + .update_end_marker = dump_lcrash_update_end_marker +}; + +struct dump_fmt dump_fmt_passthru = { + .name = "passthru", + .ops = &dump_fmt_passthru_ops +}; + +/* Filter that simply passes along any data within the range (transparent)*/ +/* Note: The start and end ranges in the table are filled in at run-time */ + +extern int dump_filter_none(int pass, unsigned long loc, unsigned long sz); + +struct dump_data_filter dump_passthru_filtertable[MAX_PASSES] = { +{.name = "passkern", .selector = dump_passthru_filter, + .level_mask = DUMP_MASK_KERN }, +{.name = "passuser", .selector = dump_passthru_filter, + .level_mask = DUMP_MASK_USED }, +{.name = "passunused", .selector = dump_passthru_filter, + .level_mask = DUMP_MASK_UNUSED }, +{.name = "none", .selector = dump_filter_none, + .level_mask = DUMP_MASK_REST } +}; + + +/* Scheme to handle data staged / preserved across a soft-boot */ +struct dump_scheme_ops dump_scheme_staged_ops = { + .configure = dump_generic_configure, + .unconfigure = dump_staged_unconfigure, + .sequencer = dump_generic_sequencer, + .iterator = dump_saved_data_iterator, + .save_data = dump_generic_save_data, + .skip_data = dump_generic_skip_data, + .write_buffer = dump_generic_write_buffer +}; + +struct dump_scheme dump_scheme_staged = { + .name = "staged", + .ops = &dump_scheme_staged_ops +}; + +/* The stage 2 dumper comprising all these */ +struct dumper dumper_stage2 = { + .name = "stage2", + .scheme = &dump_scheme_staged, + .fmt = &dump_fmt_passthru, + .compress = &dump_none_compression, + .filter = dump_passthru_filtertable, + .dev = NULL, +}; + diff --git a/drivers/dump/dump_ppc64.c b/drivers/dump/dump_ppc64.c new file mode 100644 index 000000000..7fa6d857b --- /dev/null +++ b/drivers/dump/dump_ppc64.c @@ -0,0 +1,436 @@ +/* + * Architecture specific (ppc64) functions for Linux crash dumps. + * + * Created by: Matt Robinson (yakker@sgi.com) + * + * Copyright 1999 Silicon Graphics, Inc. All rights reserved. + * + * 2.3 kernel modifications by: Matt D. Robinson (yakker@turbolinux.com) + * Copyright 2000 TurboLinux, Inc. All rights reserved. + * Copyright 2003, 2004 IBM Corporation + * + * This code is released under version 2 of the GNU GPL. + */ + +/* + * The hooks for dumping the kernel virtual memory to disk are in this + * file. Any time a modification is made to the virtual memory mechanism, + * these routines must be changed to use the new mechanisms. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "dump_methods.h" +#include +#include +#include +#include +#include +#if defined(CONFIG_KDB) && !defined(CONFIG_DUMP_MODULE) +#include +#endif + +extern cpumask_t irq_affinity[]; + +static cpumask_t saved_affinity[NR_IRQS]; + +static __s32 saved_irq_count; /* saved preempt_count() flags */ + +static int alloc_dha_stack(void) +{ + int i; + void *ptr; + + if (dump_header_asm.dha_stack[0]) + return 0; + + ptr = (void *)vmalloc(THREAD_SIZE * num_online_cpus()); + if (!ptr) { + return -ENOMEM; + } + + for (i = 0; i < num_online_cpus(); i++) { + dump_header_asm.dha_stack[i] = + (uint64_t)((unsigned long)ptr + (i * THREAD_SIZE)); + } + return 0; +} + +static int free_dha_stack(void) +{ + if (dump_header_asm.dha_stack[0]) { + vfree((void*)dump_header_asm.dha_stack[0]); + dump_header_asm.dha_stack[0] = 0; + } + return 0; +} +#ifdef CONFIG_SMP +static int dump_expect_ipi[NR_CPUS]; +static atomic_t waiting_for_dump_ipi; + +extern void stop_this_cpu(void *); +static int +dump_ipi_handler(struct pt_regs *regs) +{ + int cpu = smp_processor_id(); + + if (!dump_expect_ipi[cpu]) + return 0; + dump_save_this_cpu(regs); + atomic_dec(&waiting_for_dump_ipi); + + level_changed: + switch (dump_silence_level) { + case DUMP_HARD_SPIN_CPUS: /* Spin until dump is complete */ + while (dump_oncpu) { + barrier(); /* paranoia */ + if (dump_silence_level != DUMP_HARD_SPIN_CPUS) + goto level_changed; + cpu_relax(); /* kill time nicely */ + } + break; + + case DUMP_HALT_CPUS: /* Execute halt */ + stop_this_cpu(NULL); + break; + + case DUMP_SOFT_SPIN_CPUS: + /* Mark the task so it spins in schedule */ + set_tsk_thread_flag(current, TIF_NEED_RESCHED); + break; + } + + return 1; +} + +/* save registers on other processors + * If the other cpus don't respond we simply do not get their states. + */ +void +__dump_save_other_cpus(void) +{ + int i, cpu = smp_processor_id(); + int other_cpus = num_online_cpus()-1; + + if (other_cpus > 0) { + atomic_set(&waiting_for_dump_ipi, other_cpus); + for (i = 0; i < NR_CPUS; i++) + dump_expect_ipi[i] = (i != cpu && cpu_online(i)); + + dump_send_ipi(dump_ipi_handler); + /* + * may be we dont need to wait for NMI to be processed. + * just write out the header at the end of dumping, if + * this IPI is not processed until then, there probably + * is a problem and we just fail to capture state of + * other cpus. + */ + while (atomic_read(&waiting_for_dump_ipi) > 0) { + cpu_relax(); + } + dump_send_ipi(NULL); /* clear handler */ + } +} + +/* + * Restore old irq affinities. + */ +static void +__dump_reset_irq_affinity(void) +{ + int i; + irq_desc_t *irq_d; + + memcpy(irq_affinity, saved_affinity, NR_IRQS * sizeof(unsigned long)); + + for_each_irq(i) { + irq_d = get_irq_desc(i); + if (irq_d->handler == NULL) { + continue; + } + if (irq_d->handler->set_affinity != NULL) { + irq_d->handler->set_affinity(i, saved_affinity[i]); + } + } +} + +/* + * Routine to save the old irq affinities and change affinities of all irqs to + * the dumping cpu. + * + * NB: Need to be expanded to multiple nodes. + */ +static void +__dump_set_irq_affinity(void) +{ + int i; + cpumask_t cpu = CPU_MASK_NONE; + irq_desc_t *irq_d; + + cpu_set(smp_processor_id(), cpu); + + memcpy(saved_affinity, irq_affinity, NR_IRQS * sizeof(unsigned long)); + + for_each_irq(i) { + irq_d = get_irq_desc(i); + if (irq_d->handler == NULL) { + continue; + } + irq_affinity[i] = cpu; + if (irq_d->handler->set_affinity != NULL) { + irq_d->handler->set_affinity(i, irq_affinity[i]); + } + } +} +#else /* !CONFIG_SMP */ +#define __dump_save_other_cpus() do { } while (0) +#define __dump_set_irq_affinity() do { } while (0) +#define __dump_reset_irq_affinity() do { } while (0) +#endif /* !CONFIG_SMP */ + +void +__dump_save_regs(struct pt_regs *dest_regs, const struct pt_regs *regs) +{ + if (regs) { + memcpy(dest_regs, regs, sizeof(struct pt_regs)); + } +} + +/* + * Name: __dump_configure_header() + * Func: Configure the dump header with all proper values. + */ +int +__dump_configure_header(const struct pt_regs *regs) +{ + return (0); +} + +#if defined(CONFIG_KDB) && !defined(CONFIG_DUMP_MODULE) +int +kdb_sysdump(int argc, const char **argv, const char **envp, struct pt_regs *regs) +{ + kdb_printf("Dumping to disk...\n"); + dump("dump from kdb", regs); + kdb_printf("Dump Complete\n"); + return 0; +} +#endif + +static int dw_long(unsigned int fd, unsigned int cmd, unsigned long arg, + struct file *f) +{ + mm_segment_t old_fs = get_fs(); + int err; + unsigned long val; + + set_fs (KERNEL_DS); + err = sys_ioctl(fd, cmd, (unsigned long)&val); + set_fs (old_fs); + if (!err && put_user((unsigned int) val, (u32 *)arg)) + return -EFAULT; + return err; +} + +/* + * Name: __dump_init() + * Func: Initialize the dumping routine process. This is in case + * it's necessary in the future. + */ +void +__dump_init(uint64_t local_memory_start) +{ + int ret; + + ret = register_ioctl32_conversion(DIOSDUMPDEV, NULL); + ret |= register_ioctl32_conversion(DIOGDUMPDEV, NULL); + ret |= register_ioctl32_conversion(DIOSDUMPLEVEL, NULL); + ret |= register_ioctl32_conversion(DIOGDUMPLEVEL, dw_long); + ret |= register_ioctl32_conversion(DIOSDUMPFLAGS, NULL); + ret |= register_ioctl32_conversion(DIOGDUMPFLAGS, dw_long); + ret |= register_ioctl32_conversion(DIOSDUMPCOMPRESS, NULL); + ret |= register_ioctl32_conversion(DIOGDUMPCOMPRESS, dw_long); + ret |= register_ioctl32_conversion(DIOSTARGETIP, NULL); + ret |= register_ioctl32_conversion(DIOGTARGETIP, NULL); + ret |= register_ioctl32_conversion(DIOSTARGETPORT, NULL); + ret |= register_ioctl32_conversion(DIOGTARGETPORT, NULL); + ret |= register_ioctl32_conversion(DIOSSOURCEPORT, NULL); + ret |= register_ioctl32_conversion(DIOGSOURCEPORT, NULL); + ret |= register_ioctl32_conversion(DIOSETHADDR, NULL); + ret |= register_ioctl32_conversion(DIOGETHADDR, NULL); + ret |= register_ioctl32_conversion(DIOGDUMPOKAY, dw_long); + ret |= register_ioctl32_conversion(DIOSDUMPTAKE, NULL); + if (ret) { + printk(KERN_ERR "LKCD: registering ioctl32 translations failed\n"); + } + +#if defined(FIXME) && defined(CONFIG_KDB) && !defined(CONFIG_DUMP_MODULE) + /* This won't currently work because interrupts are off in kdb + * and the dump process doesn't understand how to recover. + */ + /* ToDo: add a command to query/set dump configuration */ + kdb_register_repeat("sysdump", kdb_sysdump, "", "use lkcd to dump the system to disk (if configured)", 0, KDB_REPEAT_NONE); +#endif + + /* return */ + return; +} + +/* + * Name: __dump_open() + * Func: Open the dump device (architecture specific). This is in + * case it's necessary in the future. + */ +void +__dump_open(void) +{ + alloc_dha_stack(); +} + + +/* + * Name: __dump_cleanup() + * Func: Free any architecture specific data structures. This is called + * when the dump module is being removed. + */ +void +__dump_cleanup(void) +{ + int ret; + + ret = unregister_ioctl32_conversion(DIOSDUMPDEV); + ret |= unregister_ioctl32_conversion(DIOGDUMPDEV); + ret |= unregister_ioctl32_conversion(DIOSDUMPLEVEL); + ret |= unregister_ioctl32_conversion(DIOGDUMPLEVEL); + ret |= unregister_ioctl32_conversion(DIOSDUMPFLAGS); + ret |= unregister_ioctl32_conversion(DIOGDUMPFLAGS); + ret |= unregister_ioctl32_conversion(DIOSDUMPCOMPRESS); + ret |= unregister_ioctl32_conversion(DIOGDUMPCOMPRESS); + ret |= unregister_ioctl32_conversion(DIOSTARGETIP); + ret |= unregister_ioctl32_conversion(DIOGTARGETIP); + ret |= unregister_ioctl32_conversion(DIOSTARGETPORT); + ret |= unregister_ioctl32_conversion(DIOGTARGETPORT); + ret |= unregister_ioctl32_conversion(DIOSSOURCEPORT); + ret |= unregister_ioctl32_conversion(DIOGSOURCEPORT); + ret |= unregister_ioctl32_conversion(DIOSETHADDR); + ret |= unregister_ioctl32_conversion(DIOGETHADDR); + ret |= unregister_ioctl32_conversion(DIOGDUMPOKAY); + ret |= unregister_ioctl32_conversion(DIOSDUMPTAKE); + if (ret) { + printk(KERN_ERR "LKCD: Unregistering ioctl32 translations failed\n"); + } + free_dha_stack(); +} + +/* + * Kludge - dump from interrupt context is unreliable (Fixme) + * + * We do this so that softirqs initiated for dump i/o + * get processed and we don't hang while waiting for i/o + * to complete or in any irq synchronization attempt. + * + * This is not quite legal of course, as it has the side + * effect of making all interrupts & softirqs triggered + * while dump is in progress complete before currently + * pending softirqs and the currently executing interrupt + * code. + */ +static inline void +irq_bh_save(void) +{ + saved_irq_count = irq_count(); + preempt_count() &= ~(HARDIRQ_MASK|SOFTIRQ_MASK); +} + +static inline void +irq_bh_restore(void) +{ + preempt_count() |= saved_irq_count; +} + +/* + * Name: __dump_irq_enable + * Func: Reset system so interrupts are enabled. + * This is used for dump methods that require interrupts + * Eventually, all methods will have interrupts disabled + * and this code can be removed. + * + * Change irq affinities + * Re-enable interrupts + */ +int +__dump_irq_enable(void) +{ + __dump_set_irq_affinity(); + irq_bh_save(); + local_irq_enable(); + return 0; +} + +/* + * Name: __dump_irq_restore + * Func: Resume the system state in an architecture-specific way. + */ +void +__dump_irq_restore(void) +{ + local_irq_disable(); + __dump_reset_irq_affinity(); + irq_bh_restore(); +} + +#if 0 +/* Cheap progress hack. It estimates pages to write and + * assumes all pages will go -- so it may get way off. + * As the progress is not displayed for other architectures, not used at this + * moment. + */ +void +__dump_progress_add_page(void) +{ + unsigned long total_pages = nr_free_pages() + nr_inactive_pages + nr_active_pages; + unsigned int percent = (dump_header.dh_num_dump_pages * 100) / total_pages; + char buf[30]; + + if (percent > last_percent && percent <= 100) { + sprintf(buf, "Dump %3d%% ", percent); + ppc64_dump_msg(0x2, buf); + last_percent = percent; + } + +} +#endif + +extern int dump_page_is_ram(unsigned long); +/* + * Name: __dump_page_valid() + * Func: Check if page is valid to dump. + */ +int +__dump_page_valid(unsigned long index) +{ + if (!pfn_valid(index)) + return 0; + + return dump_page_is_ram(index); +} + +/* + * Name: manual_handle_crashdump() + * Func: Interface for the lkcd dump command. Calls dump_execute() + */ +int +manual_handle_crashdump(void) +{ + struct pt_regs regs; + + get_current_regs(®s); + dump_execute("manual", ®s); + return 0; +} diff --git a/drivers/dump/dump_rle.c b/drivers/dump/dump_rle.c new file mode 100644 index 000000000..9d8c1bd0a --- /dev/null +++ b/drivers/dump/dump_rle.c @@ -0,0 +1,175 @@ +/* + * RLE Compression functions for kernel crash dumps. + * + * Created by: Matt Robinson (yakker@sourceforge.net) + * Copyright 2001 Matt D. Robinson. All rights reserved. + * + * This code is released under version 2 of the GNU GPL. + */ + +/* header files */ +#include +#include +#include +#include +#include +#include +#include + +/* + * Name: dump_compress_rle() + * Func: Compress a DUMP_PAGE_SIZE (hardware) page down to something more + * reasonable, if possible. This is the same routine we use in IRIX. + */ +static u16 +dump_compress_rle(const u8 *old, u16 oldsize, u8 *new, u16 newsize) +{ + u16 ri, wi, count = 0; + u_char value = 0, cur_byte; + + /* + * If the block should happen to "compress" to larger than the + * buffer size, allocate a larger one and change cur_buf_size. + */ + + wi = ri = 0; + + while (ri < oldsize) { + if (!ri) { + cur_byte = value = old[ri]; + count = 0; + } else { + if (count == 255) { + if (wi + 3 > oldsize) { + return oldsize; + } + new[wi++] = 0; + new[wi++] = count; + new[wi++] = value; + value = cur_byte = old[ri]; + count = 0; + } else { + if ((cur_byte = old[ri]) == value) { + count++; + } else { + if (count > 1) { + if (wi + 3 > oldsize) { + return oldsize; + } + new[wi++] = 0; + new[wi++] = count; + new[wi++] = value; + } else if (count == 1) { + if (value == 0) { + if (wi + 3 > oldsize) { + return oldsize; + } + new[wi++] = 0; + new[wi++] = 1; + new[wi++] = 0; + } else { + if (wi + 2 > oldsize) { + return oldsize; + } + new[wi++] = value; + new[wi++] = value; + } + } else { /* count == 0 */ + if (value == 0) { + if (wi + 2 > oldsize) { + return oldsize; + } + new[wi++] = value; + new[wi++] = value; + } else { + if (wi + 1 > oldsize) { + return oldsize; + } + new[wi++] = value; + } + } /* if count > 1 */ + + value = cur_byte; + count = 0; + + } /* if byte == value */ + + } /* if count == 255 */ + + } /* if ri == 0 */ + ri++; + + } + if (count > 1) { + if (wi + 3 > oldsize) { + return oldsize; + } + new[wi++] = 0; + new[wi++] = count; + new[wi++] = value; + } else if (count == 1) { + if (value == 0) { + if (wi + 3 > oldsize) + return oldsize; + new[wi++] = 0; + new[wi++] = 1; + new[wi++] = 0; + } else { + if (wi + 2 > oldsize) + return oldsize; + new[wi++] = value; + new[wi++] = value; + } + } else { /* count == 0 */ + if (value == 0) { + if (wi + 2 > oldsize) + return oldsize; + new[wi++] = value; + new[wi++] = value; + } else { + if (wi + 1 > oldsize) + return oldsize; + new[wi++] = value; + } + } /* if count > 1 */ + + value = cur_byte; + count = 0; + return wi; +} + +/* setup the rle compression functionality */ +static struct __dump_compress dump_rle_compression = { + .compress_type = DUMP_COMPRESS_RLE, + .compress_func = dump_compress_rle, + .compress_name = "RLE", +}; + +/* + * Name: dump_compress_rle_init() + * Func: Initialize rle compression for dumping. + */ +static int __init +dump_compress_rle_init(void) +{ + dump_register_compression(&dump_rle_compression); + return 0; +} + +/* + * Name: dump_compress_rle_cleanup() + * Func: Remove rle compression for dumping. + */ +static void __exit +dump_compress_rle_cleanup(void) +{ + dump_unregister_compression(DUMP_COMPRESS_RLE); +} + +/* module initialization */ +module_init(dump_compress_rle_init); +module_exit(dump_compress_rle_cleanup); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("LKCD Development Team "); +MODULE_DESCRIPTION("RLE compression module for crash dump driver"); diff --git a/drivers/dump/dump_scheme.c b/drivers/dump/dump_scheme.c new file mode 100644 index 000000000..de0ce783a --- /dev/null +++ b/drivers/dump/dump_scheme.c @@ -0,0 +1,383 @@ +/* + * Default single stage dump scheme methods + * + * Previously a part of dump_base.c + * + * Started: Oct 2002 - Suparna Bhattacharya + * Split and rewrote LKCD dump scheme to generic dump method + * interfaces + * Derived from original code created by + * Matt Robinson ) + * + * Contributions from SGI, IBM, HP, MCL, and others. + * + * Copyright (C) 1999 - 2002 Silicon Graphics, Inc. All rights reserved. + * Copyright (C) 2001 - 2002 Matt D. Robinson. All rights reserved. + * Copyright (C) 2002 International Business Machines Corp. + * + * This code is released under version 2 of the GNU GPL. + */ + +/* + * Implements the default dump scheme, i.e. single-stage gathering and + * saving of dump data directly to the target device, which operates in + * a push mode, where the dumping system decides what data it saves + * taking into account pre-specified dump config options. + * + * Aside: The 2-stage dump scheme, where there is a soft-reset between + * the gathering and saving phases, also reuses some of these + * default routines (see dump_overlay.c) + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include "dump_methods.h" + +extern int panic_timeout; /* time before reboot */ + +extern void dump_speedo(int); + +/* Default sequencer used during single stage dumping */ +/* Also invoked during stage 2 of soft-boot based dumping */ +int dump_generic_sequencer(void) +{ + struct dump_data_filter *filter = dump_config.dumper->filter; + int pass = 0, err = 0, save = 0; + int (*action)(unsigned long, unsigned long); + + /* + * We want to save the more critical data areas first in + * case we run out of space, encounter i/o failures, or get + * interrupted otherwise and have to give up midway + * So, run through the passes in increasing order + */ + for (;filter->selector; filter++, pass++) + { + /* Assumes passes are exclusive (even across dumpers) */ + /* Requires care when coding the selection functions */ + if ((save = filter->level_mask & dump_config.level)) + action = dump_save_data; + else + action = dump_skip_data; + + if ((err = dump_iterator(pass, action, filter)) < 0) + break; + + printk("\n %d dump pages %s of %d each in pass %d\n", + err, save ? "saved" : "skipped", DUMP_PAGE_SIZE, pass); + + } + + return (err < 0) ? err : 0; +} + +static inline struct page *dump_get_page(loff_t loc) +{ + + unsigned long page_index = loc >> PAGE_SHIFT; + + /* todo: complete this to account for ia64/discontig mem */ + /* todo: and to check for validity, ram page, no i/o mem etc */ + /* need to use pfn/physaddr equiv of kern_addr_valid */ + + /* Important: + * On ARM/XScale system, the physical address starts from + * PHYS_OFFSET, and it maybe the situation that PHYS_OFFSET != 0. + * For example on Intel's PXA250, PHYS_OFFSET = 0xa0000000. And the + * page index starts from PHYS_PFN_OFFSET. When configuring + * filter, filter->start is assigned to 0 in dump_generic_configure. + * Here we want to adjust it by adding PHYS_PFN_OFFSET to it! + */ +#ifdef CONFIG_ARM + page_index += PHYS_PFN_OFFSET; +#endif + if (__dump_page_valid(page_index)) + return pfn_to_page(page_index); + else + return NULL; + +} + +/* Default iterator: for singlestage and stage 1 of soft-boot dumping */ +/* Iterates over range of physical memory pages in DUMP_PAGE_SIZE increments */ +int dump_page_iterator(int pass, int (*action)(unsigned long, unsigned long), + struct dump_data_filter *filter) +{ + /* Todo : fix unit, type */ + loff_t loc, start, end; + int i, count = 0, err = 0; + struct page *page; + + /* Todo: Add membanks code */ + /* TBD: Check if we need to address DUMP_PAGE_SIZE < PAGE_SIZE */ + + for (i = 0; i < filter->num_mbanks; i++) { + start = filter->start[i]; + end = filter->end[i]; + for (loc = start; loc < end; loc += DUMP_PAGE_SIZE) { + dump_config.dumper->curr_loc = loc; + page = dump_get_page(loc); + if (page && filter->selector(pass, + (unsigned long) page, DUMP_PAGE_SIZE)) { + if ((err = action((unsigned long)page, + DUMP_PAGE_SIZE))) { + printk("dump_page_iterator: err %d for " + "loc 0x%llx, in pass %d\n", + err, loc, pass); + return err ? err : count; + } else + count++; + } + } + } + + return err ? err : count; +} + +/* + * Base function that saves the selected block of data in the dump + * Action taken when iterator decides that data needs to be saved + */ +int dump_generic_save_data(unsigned long loc, unsigned long sz) +{ + void *buf; + void *dump_buf = dump_config.dumper->dump_buf; + int left, bytes, ret; + + if ((ret = dump_add_data(loc, sz))) { + return ret; + } + buf = dump_config.dumper->curr_buf; + + /* If we've filled up the buffer write it out */ + if ((left = buf - dump_buf) >= DUMP_BUFFER_SIZE) { + bytes = dump_write_buffer(dump_buf, DUMP_BUFFER_SIZE); + if (bytes < DUMP_BUFFER_SIZE) { + printk("dump_write_buffer failed %d\n", bytes); + return bytes ? -ENOSPC : bytes; + } + + left -= bytes; + + /* -- A few chores to do from time to time -- */ + dump_config.dumper->count++; + + if (!(dump_config.dumper->count & 0x3f)) { + /* Update the header every one in a while */ + memset((void *)dump_buf, 'b', DUMP_BUFFER_SIZE); + if ((ret = dump_update_header()) < 0) { + /* issue warning */ + return ret; + } + printk("."); + + touch_nmi_watchdog(); + } else if (!(dump_config.dumper->count & 0x7)) { + /* Show progress so the user knows we aren't hung */ + dump_speedo(dump_config.dumper->count >> 3); + } + /* Todo: Touch/Refresh watchdog */ + + /* --- Done with periodic chores -- */ + + /* + * extra bit of copying to simplify verification + * in the second kernel boot based scheme + */ + memcpy(dump_buf - DUMP_PAGE_SIZE, dump_buf + + DUMP_BUFFER_SIZE - DUMP_PAGE_SIZE, DUMP_PAGE_SIZE); + + /* now adjust the leftover bits back to the top of the page */ + /* this case would not arise during stage 2 (passthru) */ + memset(dump_buf, 'z', DUMP_BUFFER_SIZE); + if (left) { + memcpy(dump_buf, dump_buf + DUMP_BUFFER_SIZE, left); + } + buf -= DUMP_BUFFER_SIZE; + dump_config.dumper->curr_buf = buf; + } + + return 0; +} + +int dump_generic_skip_data(unsigned long loc, unsigned long sz) +{ + /* dummy by default */ + return 0; +} + +/* + * Common low level routine to write a buffer to current dump device + * Expects checks for space etc to have been taken care of by the caller + * Operates serially at the moment for simplicity. + * TBD/Todo: Consider batching for improved throughput + */ +int dump_ll_write(void *buf, unsigned long len) +{ + long transferred = 0, last_transfer = 0; + int ret = 0; + + /* make sure device is ready */ + while ((ret = dump_dev_ready(NULL)) == -EAGAIN); + if (ret < 0) { + printk("dump_dev_ready failed !err %d\n", ret); + return ret; + } + + while (len) { + if ((last_transfer = dump_dev_write(buf, len)) <= 0) { + ret = last_transfer; + printk("dump_dev_write failed !err %d\n", + ret); + break; + } + /* wait till complete */ + while ((ret = dump_dev_ready(buf)) == -EAGAIN) + cpu_relax(); + + if (ret < 0) { + printk("i/o failed !err %d\n", ret); + break; + } + + len -= last_transfer; + buf += last_transfer; + transferred += last_transfer; + } + return (ret < 0) ? ret : transferred; +} + +/* default writeout routine for single dump device */ +/* writes out the dump data ensuring enough space is left for the end marker */ +int dump_generic_write_buffer(void *buf, unsigned long len) +{ + long written = 0; + int err = 0; + + /* check for space */ + if ((err = dump_dev_seek(dump_config.dumper->curr_offset + len + + 2*DUMP_BUFFER_SIZE)) < 0) { + printk("dump_write_buffer: insuff space after offset 0x%llx\n", + dump_config.dumper->curr_offset); + return err; + } + /* alignment check would happen as a side effect of this */ + if ((err = dump_dev_seek(dump_config.dumper->curr_offset)) < 0) + return err; + + written = dump_ll_write(buf, len); + + /* all or none */ + + if (written < len) + written = written ? -ENOSPC : written; + else + dump_config.dumper->curr_offset += len; + + return written; +} + +int dump_generic_configure(unsigned long devid) +{ + struct dump_dev *dev = dump_config.dumper->dev; + struct dump_data_filter *filter; + void *buf; + int ret = 0; + + /* Allocate the dump buffer and initialize dumper state */ + /* Assume that we get aligned addresses */ + if (!(buf = dump_alloc_mem(DUMP_BUFFER_SIZE + 3 * DUMP_PAGE_SIZE))) + return -ENOMEM; + + if ((unsigned long)buf & (PAGE_SIZE - 1)) { + /* sanity check for page aligned address */ + dump_free_mem(buf); + return -ENOMEM; /* fixme: better error code */ + } + + /* Initialize the rest of the fields */ + dump_config.dumper->dump_buf = buf + DUMP_PAGE_SIZE; + dumper_reset(); + + /* Open the dump device */ + if (!dev) + return -ENODEV; + + if ((ret = dev->ops->open(dev, devid))) { + return ret; + } + + /* Initialise the memory ranges in the dump filter */ + for (filter = dump_config.dumper->filter ;filter->selector; filter++) { + if (!filter->start[0] && !filter->end[0]) { + pg_data_t *pgdat; + int i = 0; + for_each_pgdat(pgdat) { + filter->start[i] = + (loff_t)pgdat->node_start_pfn << PAGE_SHIFT; + filter->end[i] = + (loff_t)(pgdat->node_start_pfn + pgdat->node_spanned_pages) << PAGE_SHIFT; + i++; + } + filter->num_mbanks = i; + } + } + + return 0; +} + +int dump_generic_unconfigure(void) +{ + struct dump_dev *dev = dump_config.dumper->dev; + void *buf = dump_config.dumper->dump_buf; + int ret = 0; + + pr_debug("Generic unconfigure\n"); + /* Close the dump device */ + if (dev && (ret = dev->ops->release(dev))) + return ret; + + printk("Closed dump device\n"); + + if (buf) + dump_free_mem((buf - DUMP_PAGE_SIZE)); + + dump_config.dumper->curr_buf = dump_config.dumper->dump_buf = NULL; + pr_debug("Released dump buffer\n"); + + return 0; +} + + +/* Set up the default dump scheme */ + +struct dump_scheme_ops dump_scheme_singlestage_ops = { + .configure = dump_generic_configure, + .unconfigure = dump_generic_unconfigure, + .sequencer = dump_generic_sequencer, + .iterator = dump_page_iterator, + .save_data = dump_generic_save_data, + .skip_data = dump_generic_skip_data, + .write_buffer = dump_generic_write_buffer, +}; + +struct dump_scheme dump_scheme_singlestage = { + .name = "single-stage", + .ops = &dump_scheme_singlestage_ops +}; + +/* The single stage dumper comprising all these */ +struct dumper dumper_singlestage = { + .name = "single-stage", + .scheme = &dump_scheme_singlestage, + .fmt = &dump_fmt_lcrash, + .compress = &dump_none_compression, + .filter = dump_filter_table, + .dev = NULL, +}; + diff --git a/drivers/dump/dump_setup.c b/drivers/dump/dump_setup.c new file mode 100644 index 000000000..668b2d052 --- /dev/null +++ b/drivers/dump/dump_setup.c @@ -0,0 +1,835 @@ +/* + * Standard kernel function entry points for Linux crash dumps. + * + * Created by: Matt Robinson (yakker@sourceforge.net) + * Contributions from SGI, IBM, HP, MCL, and others. + * + * Copyright (C) 1999 - 2002 Silicon Graphics, Inc. All rights reserved. + * Copyright (C) 2000 - 2002 TurboLinux, Inc. All rights reserved. + * Copyright (C) 2001 - 2002 Matt D. Robinson. All rights reserved. + * Copyright (C) 2002 Free Software Foundation, Inc. All rights reserved. + * + * This code is released under version 2 of the GNU GPL. + */ + +/* + * ----------------------------------------------------------------------- + * + * DUMP HISTORY + * + * This dump code goes back to SGI's first attempts at dumping system + * memory on SGI systems running IRIX. A few developers at SGI needed + * a way to take this system dump and analyze it, and created 'icrash', + * or IRIX Crash. The mechanism (the dumps and 'icrash') were used + * by support people to generate crash reports when a system failure + * occurred. This was vital for large system configurations that + * couldn't apply patch after patch after fix just to hope that the + * problems would go away. So the system memory, along with the crash + * dump analyzer, allowed support people to quickly figure out what the + * problem was on the system with the crash dump. + * + * In comes Linux. SGI started moving towards the open source community, + * and upon doing so, SGI wanted to take its support utilities into Linux + * with the hopes that they would end up the in kernel and user space to + * be used by SGI's customers buying SGI Linux systems. One of the first + * few products to be open sourced by SGI was LKCD, or Linux Kernel Crash + * Dumps. LKCD comprises of a patch to the kernel to enable system + * dumping, along with 'lcrash', or Linux Crash, to analyze the system + * memory dump. A few additional system scripts and kernel modifications + * are also included to make the dump mechanism and dump data easier to + * process and use. + * + * As soon as LKCD was released into the open source community, a number + * of larger companies started to take advantage of it. Today, there are + * many community members that contribute to LKCD, and it continues to + * flourish and grow as an open source project. + */ + +/* + * DUMP TUNABLES + * + * This is the list of system tunables (via /proc) that are available + * for Linux systems. All the read, write, etc., functions are listed + * here. Currently, there are a few different tunables for dumps: + * + * dump_device (used to be dumpdev): + * The device for dumping the memory pages out to. This + * may be set to the primary swap partition for disruptive dumps, + * and must be an unused partition for non-disruptive dumps. + * Todo: In the case of network dumps, this may be interpreted + * as the IP address of the netdump server to connect to. + * + * dump_compress (used to be dump_compress_pages): + * This is the flag which indicates which compression mechanism + * to use. This is a BITMASK, not an index (0,1,2,4,8,16,etc.). + * This is the current set of values: + * + * 0: DUMP_COMPRESS_NONE -- Don't compress any pages. + * 1: DUMP_COMPRESS_RLE -- This uses RLE compression. + * 2: DUMP_COMPRESS_GZIP -- This uses GZIP compression. + * + * dump_level: + * The amount of effort the dump module should make to save + * information for post crash analysis. This value is now + * a BITMASK value, not an index: + * + * 0: Do nothing, no dumping. (DUMP_LEVEL_NONE) + * + * 1: Print out the dump information to the dump header, and + * write it out to the dump_device. (DUMP_LEVEL_HEADER) + * + * 2: Write out the dump header and all kernel memory pages. + * (DUMP_LEVEL_KERN) + * + * 4: Write out the dump header and all kernel and user + * memory pages. (DUMP_LEVEL_USED) + * + * 8: Write out the dump header and all conventional/cached + * memory (RAM) pages in the system (kernel, user, free). + * (DUMP_LEVEL_ALL_RAM) + * + * 16: Write out everything, including non-conventional memory + * like firmware, proms, I/O registers, uncached memory. + * (DUMP_LEVEL_ALL) + * + * The dump_level will default to 1. + * + * dump_flags: + * These are the flags to use when talking about dumps. There + * are lots of possibilities. This is a BITMASK value, not an index. + * + * ----------------------------------------------------------------------- + */ + +#include +#include +#include +#include +#include +#include "dump_methods.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* + * ----------------------------------------------------------------------- + * V A R I A B L E S + * ----------------------------------------------------------------------- + */ + +/* Dump tunables */ +struct dump_config dump_config = { + .level = 0, + .flags = 0, + .dump_device = 0, + .dump_addr = 0, + .dumper = NULL +}; +#ifdef CONFIG_ARM +static _dump_regs_t all_regs; +#endif + +/* Global variables used in dump.h */ +/* degree of system freeze when dumping */ +enum dump_silence_levels dump_silence_level = DUMP_HARD_SPIN_CPUS; + +/* Other global fields */ +extern struct __dump_header dump_header; +struct dump_dev *dump_dev = NULL; /* Active dump device */ +static int dump_compress = 0; + +static u16 dump_compress_none(const u8 *old, u16 oldsize, u8 *new, u16 newsize); +struct __dump_compress dump_none_compression = { + .compress_type = DUMP_COMPRESS_NONE, + .compress_func = dump_compress_none, + .compress_name = "none", +}; + +/* our device operations and functions */ +static int dump_ioctl(struct inode *i, struct file *f, + unsigned int cmd, unsigned long arg); + +static struct file_operations dump_fops = { + .owner = THIS_MODULE, + .ioctl = dump_ioctl, +}; + +static struct miscdevice dump_miscdev = { + .minor = CRASH_DUMP_MINOR, + .name = "dump", + .fops = &dump_fops, +}; +MODULE_ALIAS_MISCDEV(CRASH_DUMP_MINOR); + +/* static variables */ +static int dump_okay = 0; /* can we dump out to disk? */ +static spinlock_t dump_lock = SPIN_LOCK_UNLOCKED; + +/* used for dump compressors */ +static struct list_head dump_compress_list = LIST_HEAD_INIT(dump_compress_list); + +/* list of registered dump targets */ +static struct list_head dump_target_list = LIST_HEAD_INIT(dump_target_list); + +/* lkcd info structure -- this is used by lcrash for basic system data */ +struct __lkcdinfo lkcdinfo = { + .ptrsz = (sizeof(void *) * 8), +#if defined(__LITTLE_ENDIAN) + .byte_order = __LITTLE_ENDIAN, +#else + .byte_order = __BIG_ENDIAN, +#endif + .page_shift = PAGE_SHIFT, + .page_size = PAGE_SIZE, + .page_mask = PAGE_MASK, + .page_offset = PAGE_OFFSET, +}; + +/* + * ----------------------------------------------------------------------- + * / P R O C T U N A B L E F U N C T I O N S + * ----------------------------------------------------------------------- + */ + +static int proc_dump_device(ctl_table *ctl, int write, struct file *f, + void *buffer, size_t *lenp); + +static int proc_doulonghex(ctl_table *ctl, int write, struct file *f, + void *buffer, size_t *lenp); +/* + * sysctl-tuning infrastructure. + */ +static ctl_table dump_table[] = { + { .ctl_name = CTL_DUMP_LEVEL, + .procname = DUMP_LEVEL_NAME, + .data = &dump_config.level, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_doulonghex, }, + + { .ctl_name = CTL_DUMP_FLAGS, + .procname = DUMP_FLAGS_NAME, + .data = &dump_config.flags, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_doulonghex, }, + + { .ctl_name = CTL_DUMP_COMPRESS, + .procname = DUMP_COMPRESS_NAME, + .data = &dump_compress, /* FIXME */ + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec, }, + + { .ctl_name = CTL_DUMP_DEVICE, + .procname = DUMP_DEVICE_NAME, + .mode = 0644, + .data = &dump_config.dump_device, /* FIXME */ + .maxlen = sizeof(int), + .proc_handler = proc_dump_device }, + +#ifdef CONFIG_CRASH_DUMP_MEMDEV + { .ctl_name = CTL_DUMP_ADDR, + .procname = DUMP_ADDR_NAME, + .mode = 0444, + .data = &dump_config.dump_addr, + .maxlen = sizeof(unsigned long), + .proc_handler = proc_doulonghex }, +#endif + + { 0, } +}; + +static ctl_table dump_root[] = { + { .ctl_name = KERN_DUMP, + .procname = "dump", + .mode = 0555, + .child = dump_table }, + { 0, } +}; + +static ctl_table kernel_root[] = { + { .ctl_name = CTL_KERN, + .procname = "kernel", + .mode = 0555, + .child = dump_root, }, + { 0, } +}; + +static struct ctl_table_header *sysctl_header; + +/* + * ----------------------------------------------------------------------- + * C O M P R E S S I O N F U N C T I O N S + * ----------------------------------------------------------------------- + */ + +/* + * Name: dump_compress_none() + * Func: Don't do any compression, period. + */ +static u16 +dump_compress_none(const u8 *old, u16 oldsize, u8 *new, u16 newsize) +{ + /* just return the old size */ + return oldsize; +} + + +/* + * Name: dump_execute() + * Func: Execute the dumping process. This makes sure all the appropriate + * fields are updated correctly, and calls dump_execute_memdump(), + * which does the real work. + */ +void +dump_execute(const char *panic_str, const struct pt_regs *regs) +{ + int state = -1; + unsigned long flags; + + /* make sure we can dump */ + if (!dump_okay) { + pr_info("LKCD not yet configured, can't take dump now\n"); + return; + } + + /* Exclude multiple dumps at the same time, + * and disable interrupts, some drivers may re-enable + * interrupts in with silence() + * + * Try and acquire spin lock. If successful, leave preempt + * and interrupts disabled. See spin_lock_irqsave in spinlock.h + */ + local_irq_save(flags); + if (!spin_trylock(&dump_lock)) { + local_irq_restore(flags); + pr_info("LKCD dump already in progress\n"); + return; + } + + /* Bring system into the strictest level of quiescing for min drift + * dump drivers can soften this as required in dev->ops->silence() + */ + dump_oncpu = smp_processor_id() + 1; + dump_silence_level = DUMP_HARD_SPIN_CPUS; + + state = dump_generic_execute(panic_str, regs); + + dump_oncpu = 0; + spin_unlock_irqrestore(&dump_lock, flags); + + if (state < 0) { + printk("Dump Incomplete or failed!\n"); + } else { + printk("Dump Complete; %d dump pages saved.\n", + dump_header.dh_num_dump_pages); + } +} + +/* + * Name: dump_register_compression() + * Func: Register a dump compression mechanism. + */ +void +dump_register_compression(struct __dump_compress *item) +{ + if (item) + list_add(&(item->list), &dump_compress_list); +} + +/* + * Name: dump_unregister_compression() + * Func: Remove a dump compression mechanism, and re-assign the dump + * compression pointer if necessary. + */ +void +dump_unregister_compression(int compression_type) +{ + struct list_head *tmp; + struct __dump_compress *dc; + + /* let's make sure our list is valid */ + if (compression_type != DUMP_COMPRESS_NONE) { + list_for_each(tmp, &dump_compress_list) { + dc = list_entry(tmp, struct __dump_compress, list); + if (dc->compress_type == compression_type) { + list_del(&(dc->list)); + break; + } + } + } +} + +/* + * Name: dump_compress_init() + * Func: Initialize (or re-initialize) compression scheme. + */ +static int +dump_compress_init(int compression_type) +{ + struct list_head *tmp; + struct __dump_compress *dc; + + /* try to remove the compression item */ + list_for_each(tmp, &dump_compress_list) { + dc = list_entry(tmp, struct __dump_compress, list); + if (dc->compress_type == compression_type) { + dump_config.dumper->compress = dc; + dump_compress = compression_type; + pr_debug("Dump Compress %s\n", dc->compress_name); + return 0; + } + } + + /* + * nothing on the list -- return ENODATA to indicate an error + * + * NB: + * EAGAIN: reports "Resource temporarily unavailable" which + * isn't very enlightening. + */ + printk("compression_type:%d not found\n", compression_type); + + return -ENODATA; +} + +static int +dumper_setup(unsigned long flags, unsigned long devid) +{ + int ret = 0; + + /* unconfigure old dumper if it exists */ + dump_okay = 0; + if (dump_config.dumper) { + pr_debug("Unconfiguring current dumper\n"); + dump_unconfigure(); + } + /* set up new dumper */ + if (dump_config.flags & DUMP_FLAGS_SOFTBOOT) { + printk("Configuring softboot based dump \n"); +#ifdef CONFIG_CRASH_DUMP_MEMDEV + dump_config.dumper = &dumper_stage1; +#else + printk("Requires CONFIG_CRASHDUMP_MEMDEV. Can't proceed.\n"); + return -1; +#endif + } else { + dump_config.dumper = &dumper_singlestage; + } + dump_config.dumper->dev = dump_dev; + + ret = dump_configure(devid); + if (!ret) { + dump_okay = 1; + pr_debug("%s dumper set up for dev 0x%lx\n", + dump_config.dumper->name, devid); + dump_config.dump_device = devid; + } else { + printk("%s dumper set up failed for dev 0x%lx\n", + dump_config.dumper->name, devid); + dump_config.dumper = NULL; + } + return ret; +} + +static int +dump_target_init(int target) +{ + char type[20]; + struct list_head *tmp; + struct dump_dev *dev; + + switch (target) { + case DUMP_FLAGS_DISKDUMP: + strcpy(type, "blockdev"); break; + case DUMP_FLAGS_NETDUMP: + strcpy(type, "networkdev"); break; + default: + return -1; + } + + /* + * This is a bit stupid, generating strings from flag + * and doing strcmp. This is done because 'struct dump_dev' + * has string 'type_name' and not interger 'type'. + */ + list_for_each(tmp, &dump_target_list) { + dev = list_entry(tmp, struct dump_dev, list); + if (strcmp(type, dev->type_name) == 0) { + dump_dev = dev; + return 0; + } + } + return -1; +} + +/* + * Name: dump_ioctl() + * Func: Allow all dump tunables through a standard ioctl() mechanism. + * This is far better than before, where we'd go through /proc, + * because now this will work for multiple OS and architectures. + */ +static int +dump_ioctl(struct inode *i, struct file *f, unsigned int cmd, unsigned long arg) +{ + /* check capabilities */ + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + if (!dump_config.dumper && cmd == DIOSDUMPCOMPRESS) + /* dump device must be configured first */ + return -ENODEV; + + /* + * This is the main mechanism for controlling get/set data + * for various dump device parameters. The real trick here + * is setting the dump device (DIOSDUMPDEV). That's what + * triggers everything else. + */ + switch (cmd) { + case DIOSDUMPDEV: /* set dump_device */ + pr_debug("Configuring dump device\n"); + if (!(f->f_flags & O_RDWR)) + return -EPERM; + + __dump_open(); + return dumper_setup(dump_config.flags, arg); + + + case DIOGDUMPDEV: /* get dump_device */ + return put_user((long)dump_config.dump_device, (long *)arg); + + case DIOSDUMPLEVEL: /* set dump_level */ + if (!(f->f_flags & O_RDWR)) + return -EPERM; + + /* make sure we have a positive value */ + if (arg < 0) + return -EINVAL; + + /* Fixme: clean this up */ + dump_config.level = 0; + switch ((int)arg) { + case DUMP_LEVEL_ALL: + case DUMP_LEVEL_ALL_RAM: + dump_config.level |= DUMP_MASK_UNUSED; + case DUMP_LEVEL_USED: + dump_config.level |= DUMP_MASK_USED; + case DUMP_LEVEL_KERN: + dump_config.level |= DUMP_MASK_KERN; + case DUMP_LEVEL_HEADER: + dump_config.level |= DUMP_MASK_HEADER; + case DUMP_LEVEL_NONE: + break; + default: + return (-EINVAL); + } + pr_debug("Dump Level 0x%lx\n", dump_config.level); + break; + + case DIOGDUMPLEVEL: /* get dump_level */ + /* fixme: handle conversion */ + return put_user((long)dump_config.level, (long *)arg); + + + case DIOSDUMPFLAGS: /* set dump_flags */ + /* check flags */ + if (!(f->f_flags & O_RDWR)) + return -EPERM; + + /* make sure we have a positive value */ + if (arg < 0) + return -EINVAL; + + if (dump_target_init(arg & DUMP_FLAGS_TARGETMASK) < 0) + return -EINVAL; /* return proper error */ + + dump_config.flags = arg; + + pr_debug("Dump Flags 0x%lx\n", dump_config.flags); + break; + + case DIOGDUMPFLAGS: /* get dump_flags */ + return put_user((long)dump_config.flags, (long *)arg); + + case DIOSDUMPCOMPRESS: /* set the dump_compress status */ + if (!(f->f_flags & O_RDWR)) + return -EPERM; + + return dump_compress_init((int)arg); + + case DIOGDUMPCOMPRESS: /* get the dump_compress status */ + return put_user((long)(dump_config.dumper ? + dump_config.dumper->compress->compress_type : 0), + (long *)arg); + case DIOGDUMPOKAY: /* check if dump is configured */ + return put_user((long)dump_okay, (long *)arg); + + case DIOSDUMPTAKE: /* Trigger a manual dump */ + /* Do not proceed if lkcd not yet configured */ + if(!dump_okay) { + printk("LKCD not yet configured. Cannot take manual dump\n"); + return -ENODEV; + } + + /* Take the dump */ + return manual_handle_crashdump(); + + default: + /* + * these are network dump specific ioctls, let the + * module handle them. + */ + return dump_dev_ioctl(cmd, arg); + } + return 0; +} + +/* + * Handle special cases for dump_device + * changing dump device requires doing an opening the device + */ +static int +proc_dump_device(ctl_table *ctl, int write, struct file *f, + void *buffer, size_t *lenp) +{ + int *valp = ctl->data; + int oval = *valp; + int ret = -EPERM; + + /* same permission checks as ioctl */ + if (capable(CAP_SYS_ADMIN)) { + ret = proc_doulonghex(ctl, write, f, buffer, lenp); + if (ret == 0 && write && *valp != oval) { + /* need to restore old value to close properly */ + dump_config.dump_device = (dev_t) oval; + __dump_open(); + ret = dumper_setup(dump_config.flags, (dev_t) *valp); + } + } + + return ret; +} + +/* All for the want of a proc_do_xxx routine which prints values in hex */ +static int +proc_doulonghex(ctl_table *ctl, int write, struct file *f, + void *buffer, size_t *lenp) +{ +#define TMPBUFLEN 20 + unsigned long *i; + size_t len, left; + char buf[TMPBUFLEN]; + + if (!ctl->data || !ctl->maxlen || !*lenp || (f->f_pos)) { + *lenp = 0; + return 0; + } + + i = (unsigned long *) ctl->data; + left = *lenp; + + sprintf(buf, "0x%lx\n", (*i)); + len = strlen(buf); + if (len > left) + len = left; + if(copy_to_user(buffer, buf, len)) + return -EFAULT; + + left -= len; + *lenp -= left; + f->f_pos += *lenp; + return 0; +} + +/* + * ----------------------------------------------------------------------- + * I N I T F U N C T I O N S + * ----------------------------------------------------------------------- + */ + +/* + * These register and unregister routines are exported for modules + * to register their dump drivers (like block, net etc) + */ +int +dump_register_device(struct dump_dev *ddev) +{ + struct list_head *tmp; + struct dump_dev *dev; + + list_for_each(tmp, &dump_target_list) { + dev = list_entry(tmp, struct dump_dev, list); + if (strcmp(ddev->type_name, dev->type_name) == 0) { + printk("Target type %s already registered\n", + dev->type_name); + return -1; /* return proper error */ + } + } + list_add(&(ddev->list), &dump_target_list); + + return 0; +} + +void +dump_unregister_device(struct dump_dev *ddev) +{ + list_del(&(ddev->list)); + if (ddev != dump_dev) + return; + + dump_okay = 0; + + if (dump_config.dumper) + dump_unconfigure(); + + dump_config.flags &= ~DUMP_FLAGS_TARGETMASK; + dump_okay = 0; + dump_dev = NULL; + dump_config.dumper = NULL; +} + +static int panic_event(struct notifier_block *this, unsigned long event, + void *ptr) +{ +#ifdef CONFIG_ARM + get_current_general_regs(&all_regs); + get_current_cp14_regs(&all_regs); + get_current_cp15_regs(&all_regs); + dump_execute((const char *)ptr, &all_regs); +#else + struct pt_regs regs; + + get_current_regs(®s); + dump_execute((const char *)ptr, ®s); +#endif + return 0; +} + +extern struct notifier_block *panic_notifier_list; +static int panic_event(struct notifier_block *, unsigned long, void *); +static struct notifier_block panic_block = { + .notifier_call = panic_event, +}; + +#ifdef CONFIG_MAGIC_SYSRQ +/* Sysrq handler */ +static void sysrq_handle_crashdump(int key, struct pt_regs *pt_regs, + struct tty_struct *tty) { + dump_execute("sysrq", pt_regs); +} + +static struct sysrq_key_op sysrq_crashdump_op = { + .handler = sysrq_handle_crashdump, + .help_msg = "Dump", + .action_msg = "Starting crash dump", +}; +#endif + +static inline void +dump_sysrq_register(void) +{ +#ifdef CONFIG_MAGIC_SYSRQ + __sysrq_lock_table(); + __sysrq_put_key_op(DUMP_SYSRQ_KEY, &sysrq_crashdump_op); + __sysrq_unlock_table(); +#endif +} + +static inline void +dump_sysrq_unregister(void) +{ +#ifdef CONFIG_MAGIC_SYSRQ + __sysrq_lock_table(); + if (__sysrq_get_key_op(DUMP_SYSRQ_KEY) == &sysrq_crashdump_op) + __sysrq_put_key_op(DUMP_SYSRQ_KEY, NULL); + __sysrq_unlock_table(); +#endif +} + +/* + * Name: dump_init() + * Func: Initialize the dump process. This will set up any architecture + * dependent code. The big key is we need the memory offsets before + * the page table is initialized, because the base memory offset + * is changed after paging_init() is called. + */ +static int __init +dump_init(void) +{ + struct sysinfo info; + int err; + + /* try to create our dump device */ + err = misc_register(&dump_miscdev); + if (err) { + printk("cannot register dump character device!\n"); + return err; + } + + __dump_init((u64)PAGE_OFFSET); + + /* set the dump_compression_list structure up */ + dump_register_compression(&dump_none_compression); + + /* grab the total memory size now (not if/when we crash) */ + si_meminfo(&info); + + /* set the memory size */ + dump_header.dh_memory_size = (u64)info.totalram; + + sysctl_header = register_sysctl_table(kernel_root, 0); + dump_sysrq_register(); + + notifier_chain_register(&panic_notifier_list, &panic_block); + dump_function_ptr = dump_execute; + + pr_info("Crash dump driver initialized.\n"); + return 0; +} + +static void __exit +dump_cleanup(void) +{ + dump_okay = 0; + + if (dump_config.dumper) + dump_unconfigure(); + + /* arch-specific cleanup routine */ + __dump_cleanup(); + + /* ignore errors while unregistering -- since can't do anything */ + unregister_sysctl_table(sysctl_header); + misc_deregister(&dump_miscdev); + dump_sysrq_unregister(); + notifier_chain_unregister(&panic_notifier_list, &panic_block); + dump_function_ptr = NULL; +} + +EXPORT_SYMBOL(dump_register_compression); +EXPORT_SYMBOL(dump_unregister_compression); +EXPORT_SYMBOL(dump_register_device); +EXPORT_SYMBOL(dump_unregister_device); +EXPORT_SYMBOL(dump_config); +EXPORT_SYMBOL(dump_silence_level); + +EXPORT_SYMBOL(__dump_irq_enable); +EXPORT_SYMBOL(__dump_irq_restore); + +MODULE_AUTHOR("Matt D. Robinson "); +MODULE_DESCRIPTION("Linux Kernel Crash Dump (LKCD) driver"); +MODULE_LICENSE("GPL"); + +module_init(dump_init); +module_exit(dump_cleanup); diff --git a/drivers/i2c/busses/i2c-ixp42x.c b/drivers/i2c/busses/i2c-ixp42x.c deleted file mode 100644 index 59fcb70fd..000000000 --- a/drivers/i2c/busses/i2c-ixp42x.c +++ /dev/null @@ -1,176 +0,0 @@ -/* - * drivers/i2c/i2c-adap-ixp42x.c - * - * Intel's IXP42x XScale NPU chipsets (IXP420, 421, 422, 425) do not have - * an on board I2C controller but provide 16 GPIO pins that are often - * used to create an I2C bus. This driver provides an i2c_adapter - * interface that plugs in under algo_bit and drives the GPIO pins - * as instructed by the alogorithm driver. - * - * Author: Deepak Saxena - * - * Copyright (c) 2003-2004 MontaVista Software Inc. - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - * - * NOTE: Since different platforms will use different GPIO pins for - * I2C, this driver uses an IXP42x-specific platform_data - * pointer to pass the GPIO numbers to the driver. This - * allows us to support all the different IXP42x platforms - * w/o having to put #ifdefs in this driver. - * - * See arch/arm/mach-ixp42x/ixdp425.c for an example of building a - * device list and filling in the ixp42x_i2c_pins data structure - * that is passed as the platform_data to this driver. - */ - -#include -#include -#include -#include -#include -#include - -#include /* Pick up IXP42x-specific bits */ - -static inline int ixp42x_scl_pin(void *data) -{ - return ((struct ixp42x_i2c_pins*)data)->scl_pin; -} - -static inline int ixp42x_sda_pin(void *data) -{ - return ((struct ixp42x_i2c_pins*)data)->sda_pin; -} - -static void ixp42x_bit_setscl(void *data, int val) -{ - gpio_line_set(ixp42x_scl_pin(data), 0); - gpio_line_config(ixp42x_scl_pin(data), - val ? IXP425_GPIO_IN : IXP425_GPIO_OUT ); -} - -static void ixp42x_bit_setsda(void *data, int val) -{ - gpio_line_set(ixp42x_sda_pin(data), 0); - gpio_line_config(ixp42x_sda_pin(data), - val ? IXP425_GPIO_IN : IXP425_GPIO_OUT ); -} - -static int ixp42x_bit_getscl(void *data) -{ - int scl; - - gpio_line_config(ixp42x_scl_pin(data), IXP425_GPIO_IN ); - gpio_line_get(ixp42x_scl_pin(data), &scl); - - return scl; -} - -static int ixp42x_bit_getsda(void *data) -{ - int sda; - - gpio_line_config(ixp42x_sda_pin(data), IXP425_GPIO_IN ); - gpio_line_get(ixp42x_sda_pin(data), &sda); - - return sda; -} - -struct ixp42x_i2c_data { - struct ixp42x_i2c_pins *gpio_pins; - struct i2c_adapter adapter; - struct i2c_algo_bit_data algo_data; -}; - -static int ixp42x_i2c_remove(struct device *dev) -{ - struct platform_device *plat_dev = to_platform_device(dev); - struct ixp42x_i2c_data *drv_data = dev_get_drvdata(&plat_dev->dev); - - dev_set_drvdata(&plat_dev->dev, NULL); - - i2c_bit_del_bus(&drv_data->adapter); - - kfree(drv_data); - - return 0; -} - -static int ixp42x_i2c_probe(struct device *dev) -{ - int err; - struct platform_device *plat_dev = to_platform_device(dev); - struct ixp42x_i2c_pins *gpio = plat_dev->dev.platform_data; - struct ixp42x_i2c_data *drv_data = - kmalloc(sizeof(struct ixp42x_i2c_data), GFP_KERNEL); - - if(!drv_data) - return -ENOMEM; - - memzero(drv_data, sizeof(struct ixp42x_i2c_data)); - drv_data->gpio_pins = gpio; - - /* - * We could make a lot of these structures static, but - * certain platforms may have multiple GPIO-based I2C - * buses for various device domains, so we need per-device - * algo_data->data. - */ - drv_data->algo_data.data = gpio; - drv_data->algo_data.setsda = ixp42x_bit_setsda; - drv_data->algo_data.setscl = ixp42x_bit_setscl; - drv_data->algo_data.getsda = ixp42x_bit_getsda; - drv_data->algo_data.getscl = ixp42x_bit_getscl; - drv_data->algo_data.udelay = 10; - drv_data->algo_data.mdelay = 10; - drv_data->algo_data.timeout = 100; - - drv_data->adapter.id = I2C_HW_B_IXP425, - drv_data->adapter.algo_data = &drv_data->algo_data, - - drv_data->adapter.dev.parent = &plat_dev->dev; - - gpio_line_config(gpio->scl_pin, IXP425_GPIO_IN); - gpio_line_config(gpio->sda_pin, IXP425_GPIO_IN); - gpio_line_set(gpio->scl_pin, 0); - gpio_line_set(gpio->sda_pin, 0); - - if ((err = i2c_bit_add_bus(&drv_data->adapter) != 0)) { - printk(KERN_ERR "ERROR: Could not install %s\n", dev->bus_id); - - kfree(drv_data); - return err; - } - - dev_set_drvdata(&plat_dev->dev, drv_data); - - return 0; -} - -static struct device_driver ixp42x_i2c_driver = { - .name = "IXP42X-I2C", - .bus = &platform_bus_type, - .probe = ixp42x_i2c_probe, - .remove = ixp42x_i2c_remove, -}; - -static int __init ixp42x_i2c_init(void) -{ - return driver_register(&ixp42x_i2c_driver); -} - -static void __exit ixp42x_i2c_exit(void) -{ - driver_unregister(&ixp42x_i2c_driver); -} - -module_init(ixp42x_i2c_init); -module_exit(ixp42x_i2c_exit); - -MODULE_DESCRIPTION("GPIO-based I2C driver for IXP42x systems"); -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Deepak Saxena "); - diff --git a/drivers/i2c/busses/i2c-ixp4xx.c b/drivers/i2c/busses/i2c-ixp4xx.c new file mode 100644 index 000000000..d8bfd59c1 --- /dev/null +++ b/drivers/i2c/busses/i2c-ixp4xx.c @@ -0,0 +1,181 @@ +/* + * drivers/i2c/i2c-adap-ixp4xx.c + * + * Intel's IXP4xx XScale NPU chipsets (IXP420, 421, 422, 425) do not have + * an on board I2C controller but provide 16 GPIO pins that are often + * used to create an I2C bus. This driver provides an i2c_adapter + * interface that plugs in under algo_bit and drives the GPIO pins + * as instructed by the alogorithm driver. + * + * Author: Deepak Saxena + * + * Copyright (c) 2003-2004 MontaVista Software Inc. + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + * + * NOTE: Since different platforms will use different GPIO pins for + * I2C, this driver uses an IXP4xx-specific platform_data + * pointer to pass the GPIO numbers to the driver. This + * allows us to support all the different IXP4xx platforms + * w/o having to put #ifdefs in this driver. + * + * See arch/arm/mach-ixp4xx/ixdp425.c for an example of building a + * device list and filling in the ixp4xx_i2c_pins data structure + * that is passed as the platform_data to this driver. + */ + +#include +#ifdef CONFIG_I2C_DEBUG_BUS +#define DEBUG 1 +#endif + +#include +#include +#include +#include +#include +#include + +#include /* Pick up IXP4xx-specific bits */ + +static inline int ixp4xx_scl_pin(void *data) +{ + return ((struct ixp4xx_i2c_pins*)data)->scl_pin; +} + +static inline int ixp4xx_sda_pin(void *data) +{ + return ((struct ixp4xx_i2c_pins*)data)->sda_pin; +} + +static void ixp4xx_bit_setscl(void *data, int val) +{ + gpio_line_set(ixp4xx_scl_pin(data), 0); + gpio_line_config(ixp4xx_scl_pin(data), + val ? IXP4XX_GPIO_IN : IXP4XX_GPIO_OUT ); +} + +static void ixp4xx_bit_setsda(void *data, int val) +{ + gpio_line_set(ixp4xx_sda_pin(data), 0); + gpio_line_config(ixp4xx_sda_pin(data), + val ? IXP4XX_GPIO_IN : IXP4XX_GPIO_OUT ); +} + +static int ixp4xx_bit_getscl(void *data) +{ + int scl; + + gpio_line_config(ixp4xx_scl_pin(data), IXP4XX_GPIO_IN ); + gpio_line_get(ixp4xx_scl_pin(data), &scl); + + return scl; +} + +static int ixp4xx_bit_getsda(void *data) +{ + int sda; + + gpio_line_config(ixp4xx_sda_pin(data), IXP4XX_GPIO_IN ); + gpio_line_get(ixp4xx_sda_pin(data), &sda); + + return sda; +} + +struct ixp4xx_i2c_data { + struct ixp4xx_i2c_pins *gpio_pins; + struct i2c_adapter adapter; + struct i2c_algo_bit_data algo_data; +}; + +static int ixp4xx_i2c_remove(struct device *dev) +{ + struct platform_device *plat_dev = to_platform_device(dev); + struct ixp4xx_i2c_data *drv_data = dev_get_drvdata(&plat_dev->dev); + + dev_set_drvdata(&plat_dev->dev, NULL); + + i2c_bit_del_bus(&drv_data->adapter); + + kfree(drv_data); + + return 0; +} + +static int ixp4xx_i2c_probe(struct device *dev) +{ + int err; + struct platform_device *plat_dev = to_platform_device(dev); + struct ixp4xx_i2c_pins *gpio = plat_dev->dev.platform_data; + struct ixp4xx_i2c_data *drv_data = + kmalloc(sizeof(struct ixp4xx_i2c_data), GFP_KERNEL); + + if(!drv_data) + return -ENOMEM; + + memzero(drv_data, sizeof(struct ixp4xx_i2c_data)); + drv_data->gpio_pins = gpio; + + /* + * We could make a lot of these structures static, but + * certain platforms may have multiple GPIO-based I2C + * buses for various device domains, so we need per-device + * algo_data->data. + */ + drv_data->algo_data.data = gpio; + drv_data->algo_data.setsda = ixp4xx_bit_setsda; + drv_data->algo_data.setscl = ixp4xx_bit_setscl; + drv_data->algo_data.getsda = ixp4xx_bit_getsda; + drv_data->algo_data.getscl = ixp4xx_bit_getscl; + drv_data->algo_data.udelay = 10; + drv_data->algo_data.mdelay = 10; + drv_data->algo_data.timeout = 100; + + drv_data->adapter.id = I2C_HW_B_IXP4XX, + drv_data->adapter.algo_data = &drv_data->algo_data, + + drv_data->adapter.dev.parent = &plat_dev->dev; + + gpio_line_config(gpio->scl_pin, IXP4XX_GPIO_IN); + gpio_line_config(gpio->sda_pin, IXP4XX_GPIO_IN); + gpio_line_set(gpio->scl_pin, 0); + gpio_line_set(gpio->sda_pin, 0); + + if ((err = i2c_bit_add_bus(&drv_data->adapter) != 0)) { + printk(KERN_ERR "ERROR: Could not install %s\n", dev->bus_id); + + kfree(drv_data); + return err; + } + + dev_set_drvdata(&plat_dev->dev, drv_data); + + return 0; +} + +static struct device_driver ixp4xx_i2c_driver = { + .name = "IXP4XX-I2C", + .bus = &platform_bus_type, + .probe = ixp4xx_i2c_probe, + .remove = ixp4xx_i2c_remove, +}; + +static int __init ixp4xx_i2c_init(void) +{ + return driver_register(&ixp4xx_i2c_driver); +} + +static void __exit ixp4xx_i2c_exit(void) +{ + driver_unregister(&ixp4xx_i2c_driver); +} + +module_init(ixp4xx_i2c_init); +module_exit(ixp4xx_i2c_exit); + +MODULE_DESCRIPTION("GPIO-based I2C adapter for IXP4xx systems"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Deepak Saxena "); + diff --git a/drivers/i2c/chips/max1619.c b/drivers/i2c/chips/max1619.c new file mode 100644 index 000000000..0f8a5ac5c --- /dev/null +++ b/drivers/i2c/chips/max1619.c @@ -0,0 +1,378 @@ +/* + * max1619.c - Part of lm_sensors, Linux kernel modules for hardware + * monitoring + * Copyright (C) 2003-2004 Alexey Fisher + * Jean Delvare + * + * Based on the lm90 driver. The MAX1619 is a sensor chip made by Maxim. + * It reports up to two temperatures (its own plus up to + * one external one). Complete datasheet can be + * obtained from Maxim's website at: + * http://pdfserv.maxim-ic.com/en/ds/MAX1619.pdf + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + +#include +#include +#include +#include +#include +#include + + +static unsigned short normal_i2c[] = { I2C_CLIENT_END }; +static unsigned short normal_i2c_range[] = { 0x18, 0x1a, 0x29, 0x2b, + 0x4c, 0x4e, I2C_CLIENT_END }; +static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; +static unsigned int normal_isa_range[] = { I2C_CLIENT_ISA_END }; + +/* + * Insmod parameters + */ + +SENSORS_INSMOD_1(max1619); + +/* + * The MAX1619 registers + */ + +#define MAX1619_REG_R_MAN_ID 0xFE +#define MAX1619_REG_R_CHIP_ID 0xFF +#define MAX1619_REG_R_CONFIG 0x03 +#define MAX1619_REG_W_CONFIG 0x09 +#define MAX1619_REG_R_CONVRATE 0x04 +#define MAX1619_REG_W_CONVRATE 0x0A +#define MAX1619_REG_R_STATUS 0x02 +#define MAX1619_REG_R_LOCAL_TEMP 0x00 +#define MAX1619_REG_R_REMOTE_TEMP 0x01 +#define MAX1619_REG_R_REMOTE_HIGH 0x07 +#define MAX1619_REG_W_REMOTE_HIGH 0x0D +#define MAX1619_REG_R_REMOTE_LOW 0x08 +#define MAX1619_REG_W_REMOTE_LOW 0x0E +#define MAX1619_REG_R_REMOTE_CRIT 0x10 +#define MAX1619_REG_W_REMOTE_CRIT 0x12 +#define MAX1619_REG_R_TCRIT_HYST 0x11 +#define MAX1619_REG_W_TCRIT_HYST 0x13 + +/* + * Conversions and various macros + */ + +#define TEMP_FROM_REG(val) ((val & 0x80 ? val-0x100 : val) * 1000) +#define TEMP_TO_REG(val) ((val < 0 ? val+0x100*1000 : val) / 1000) + +/* + * Functions declaration + */ + +static int max1619_attach_adapter(struct i2c_adapter *adapter); +static int max1619_detect(struct i2c_adapter *adapter, int address, + int kind); +static void max1619_init_client(struct i2c_client *client); +static int max1619_detach_client(struct i2c_client *client); +static struct max1619_data *max1619_update_device(struct device *dev); + +/* + * Driver data (common to all clients) + */ + +static struct i2c_driver max1619_driver = { + .owner = THIS_MODULE, + .name = "max1619", + .flags = I2C_DF_NOTIFY, + .attach_adapter = max1619_attach_adapter, + .detach_client = max1619_detach_client, +}; + +/* + * Client data (each client gets its own) + */ + +struct max1619_data { + struct i2c_client client; + struct semaphore update_lock; + char valid; /* zero until following fields are valid */ + unsigned long last_updated; /* in jiffies */ + + /* registers values */ + u8 temp_input1; /* local */ + u8 temp_input2, temp_low2, temp_high2; /* remote */ + u8 temp_crit2; + u8 temp_hyst2; + u8 alarms; +}; + +/* + * Internal variables + */ + +static int max1619_id = 0; + +/* + * Sysfs stuff + */ + +#define show_temp(value) \ +static ssize_t show_##value(struct device *dev, char *buf) \ +{ \ + struct max1619_data *data = max1619_update_device(dev); \ + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->value)); \ +} +show_temp(temp_input1); +show_temp(temp_input2); +show_temp(temp_low2); +show_temp(temp_high2); +show_temp(temp_crit2); +show_temp(temp_hyst2); + +#define set_temp2(value, reg) \ +static ssize_t set_##value(struct device *dev, const char *buf, \ + size_t count) \ +{ \ + struct i2c_client *client = to_i2c_client(dev); \ + struct max1619_data *data = i2c_get_clientdata(client); \ + data->value = TEMP_TO_REG(simple_strtol(buf, NULL, 10)); \ + i2c_smbus_write_byte_data(client, reg, data->value); \ + return count; \ +} + +set_temp2(temp_low2, MAX1619_REG_W_REMOTE_LOW); +set_temp2(temp_high2, MAX1619_REG_W_REMOTE_HIGH); +set_temp2(temp_crit2, MAX1619_REG_W_REMOTE_CRIT); +set_temp2(temp_hyst2, MAX1619_REG_W_TCRIT_HYST); + +static ssize_t show_alarms(struct device *dev, char *buf) +{ + struct max1619_data *data = max1619_update_device(dev); + return sprintf(buf, "%d\n", data->alarms); +} + +static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp_input1, NULL); +static DEVICE_ATTR(temp2_input, S_IRUGO, show_temp_input2, NULL); +static DEVICE_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_temp_low2, + set_temp_low2); +static DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_temp_high2, + set_temp_high2); +static DEVICE_ATTR(temp2_crit, S_IWUSR | S_IRUGO, show_temp_crit2, + set_temp_crit2); +static DEVICE_ATTR(temp2_crit_hyst, S_IWUSR | S_IRUGO, show_temp_hyst2, + set_temp_hyst2); +static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); + +/* + * Real code + */ + +static int max1619_attach_adapter(struct i2c_adapter *adapter) +{ + if (!(adapter->class & I2C_CLASS_HWMON)) + return 0; + return i2c_detect(adapter, &addr_data, max1619_detect); +} + +/* + * The following function does more than just detection. If detection + * succeeds, it also registers the new chip. + */ +static int max1619_detect(struct i2c_adapter *adapter, int address, int kind) +{ + struct i2c_client *new_client; + struct max1619_data *data; + int err = 0; + const char *name = ""; + u8 reg_config=0, reg_convrate=0, reg_status=0; + u8 man_id, chip_id; + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + goto exit; + + if (!(data = kmalloc(sizeof(struct max1619_data), GFP_KERNEL))) { + err = -ENOMEM; + goto exit; + } + memset(data, 0, sizeof(struct max1619_data)); + + /* The common I2C client data is placed right before the + MAX1619-specific data. */ + new_client = &data->client; + i2c_set_clientdata(new_client, data); + new_client->addr = address; + new_client->adapter = adapter; + new_client->driver = &max1619_driver; + new_client->flags = 0; + + /* + * Now we do the remaining detection. A negative kind means that + * the driver was loaded with no force parameter (default), so we + * must both detect and identify the chip. A zero kind means that + * the driver was loaded with the force parameter, the detection + * step shall be skipped. A positive kind means that the driver + * was loaded with the force parameter and a given kind of chip is + * requested, so both the detection and the identification steps + * are skipped. + */ + if (kind < 0) { /* detection */ + reg_config = i2c_smbus_read_byte_data(new_client, + MAX1619_REG_R_CONFIG); + reg_convrate = i2c_smbus_read_byte_data(new_client, + MAX1619_REG_R_CONVRATE); + reg_status = i2c_smbus_read_byte_data(new_client, + MAX1619_REG_R_STATUS); + if ((reg_config & 0x03) != 0x00 + || reg_convrate > 0x07 || (reg_status & 0x61 ) !=0x00) { + dev_dbg(&adapter->dev, + "MAX1619 detection failed at 0x%02x.\n", + address); + goto exit_free; + } + } + + if (kind <= 0) { /* identification */ + + man_id = i2c_smbus_read_byte_data(new_client, + MAX1619_REG_R_MAN_ID); + chip_id = i2c_smbus_read_byte_data(new_client, + MAX1619_REG_R_CHIP_ID); + + if ((man_id == 0x4D) && (chip_id == 0x04)){ + kind = max1619; + } + } + + if (kind <= 0) { /* identification failed */ + dev_info(&adapter->dev, + "Unsupported chip (man_id=0x%02X, " + "chip_id=0x%02X).\n", man_id, chip_id); + goto exit_free; + } + + + if (kind == max1619){ + name = "max1619"; + } + + /* We can fill in the remaining client fields */ + strlcpy(new_client->name, name, I2C_NAME_SIZE); + new_client->id = max1619_id++; + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto exit_free; + + /* Initialize the MAX1619 chip */ + max1619_init_client(new_client); + + /* Register sysfs hooks */ + device_create_file(&new_client->dev, &dev_attr_temp1_input); + device_create_file(&new_client->dev, &dev_attr_temp2_input); + device_create_file(&new_client->dev, &dev_attr_temp2_min); + device_create_file(&new_client->dev, &dev_attr_temp2_max); + device_create_file(&new_client->dev, &dev_attr_temp2_crit); + device_create_file(&new_client->dev, &dev_attr_temp2_crit_hyst); + device_create_file(&new_client->dev, &dev_attr_alarms); + + return 0; + +exit_free: + kfree(data); +exit: + return err; +} + +static void max1619_init_client(struct i2c_client *client) +{ + u8 config; + + /* + * Start the conversions. + */ + i2c_smbus_write_byte_data(client, MAX1619_REG_W_CONVRATE, + 5); /* 2 Hz */ + config = i2c_smbus_read_byte_data(client, MAX1619_REG_R_CONFIG); + if (config & 0x40) + i2c_smbus_write_byte_data(client, MAX1619_REG_W_CONFIG, + config & 0xBF); /* run */ +} + +static int max1619_detach_client(struct i2c_client *client) +{ + int err; + + if ((err = i2c_detach_client(client))) { + dev_err(&client->dev, "Client deregistration failed, " + "client not detached.\n"); + return err; + } + + kfree(i2c_get_clientdata(client)); + return 0; +} + +static struct max1619_data *max1619_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct max1619_data *data = i2c_get_clientdata(client); + + down(&data->update_lock); + + if ((jiffies - data->last_updated > HZ * 2) || + (jiffies < data->last_updated) || + !data->valid) { + + dev_dbg(&client->dev, "Updating max1619 data.\n"); + data->temp_input1 = i2c_smbus_read_byte_data(client, + MAX1619_REG_R_LOCAL_TEMP); + data->temp_input2 = i2c_smbus_read_byte_data(client, + MAX1619_REG_R_REMOTE_TEMP); + data->temp_high2 = i2c_smbus_read_byte_data(client, + MAX1619_REG_R_REMOTE_HIGH); + data->temp_low2 = i2c_smbus_read_byte_data(client, + MAX1619_REG_R_REMOTE_LOW); + data->temp_crit2 = i2c_smbus_read_byte_data(client, + MAX1619_REG_R_REMOTE_CRIT); + data->temp_hyst2 = i2c_smbus_read_byte_data(client, + MAX1619_REG_R_TCRIT_HYST); + data->alarms = i2c_smbus_read_byte_data(client, + MAX1619_REG_R_STATUS); + + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); + + return data; +} + +static int __init sensors_max1619_init(void) +{ + return i2c_add_driver(&max1619_driver); +} + +static void __exit sensors_max1619_exit(void) +{ + i2c_del_driver(&max1619_driver); +} + +MODULE_AUTHOR("Alexey Fisher and" + "Jean Delvare "); +MODULE_DESCRIPTION("MAX1619 sensor driver"); +MODULE_LICENSE("GPL"); + +module_init(sensors_max1619_init); +module_exit(sensors_max1619_exit); diff --git a/drivers/i2c/chips/rtc8564.c b/drivers/i2c/chips/rtc8564.c new file mode 100644 index 000000000..0fa55d45e --- /dev/null +++ b/drivers/i2c/chips/rtc8564.c @@ -0,0 +1,396 @@ +/* + * linux/drivers/i2c/chips/rtc8564.c + * + * Copyright (C) 2002-2004 Stefan Eletzhofer + * + * based on linux/drivers/acron/char/pcf8583.c + * Copyright (C) 2000 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Driver for system3's EPSON RTC 8564 chip + */ +#include +#include +#include +#include +#include +#include /* get the user-level API */ +#include +#include + +#include "rtc8564.h" + +#ifdef DEBUG +# define _DBG(x, fmt, args...) do{ if (debug>=x) printk(KERN_DEBUG"%s: " fmt "\n", __FUNCTION__, ##args); } while(0); +#else +# define _DBG(x, fmt, args...) do { } while(0); +#endif + +#define _DBGRTCTM(x, rtctm) if (debug>=x) printk("%s: secs=%d, mins=%d, hours=%d, mday=%d, " \ + "mon=%d, year=%d, wday=%d VL=%d\n", __FUNCTION__, \ + (rtctm).secs, (rtctm).mins, (rtctm).hours, (rtctm).mday, \ + (rtctm).mon, (rtctm).year, (rtctm).wday, (rtctm).vl); + +struct rtc8564_data { + struct i2c_client client; + u16 ctrl; +}; + +static inline u8 _rtc8564_ctrl1(struct i2c_client *client) +{ + struct rtc8564_data *data = i2c_get_clientdata(client); + return data->ctrl & 0xff; +} +static inline u8 _rtc8564_ctrl2(struct i2c_client *client) +{ + struct rtc8564_data *data = i2c_get_clientdata(client); + return (data->ctrl & 0xff00) >> 8; +} + +#define CTRL1(c) _rtc8564_ctrl1(c) +#define CTRL2(c) _rtc8564_ctrl2(c) + +#define BCD_TO_BIN(val) (((val)&15) + ((val)>>4)*10) +#define BIN_TO_BCD(val) ((((val)/10)<<4) + (val)%10) + +static int debug = 0; +MODULE_PARM(debug, "i"); + +static struct i2c_driver rtc8564_driver; + +static unsigned short ignore[] = { I2C_CLIENT_END }; +static unsigned short normal_addr[] = { 0x51, I2C_CLIENT_END }; + +static struct i2c_client_address_data addr_data = { + .normal_i2c = normal_addr, + .normal_i2c_range = ignore, + .probe = ignore, + .probe_range = ignore, + .ignore = ignore, + .ignore_range = ignore, + .force = ignore, +}; + +static int rtc8564_read_mem(struct i2c_client *client, struct mem *mem); +static int rtc8564_write_mem(struct i2c_client *client, struct mem *mem); + +static int rtc8564_read(struct i2c_client *client, unsigned char adr, + unsigned char *buf, unsigned char len) +{ + int ret = -EIO; + unsigned char addr[1] = { adr }; + struct i2c_msg msgs[2] = { + {client->addr, 0, 1, addr}, + {client->addr, I2C_M_RD, len, buf} + }; + + _DBG(1, "client=%p, adr=%d, buf=%p, len=%d", client, adr, buf, len); + + if (!buf || !client) { + ret = -EINVAL; + goto done; + } + + ret = i2c_transfer(client->adapter, msgs, 2); + if (ret == 2) { + ret = 0; + } + +done: + return ret; +} + +static int rtc8564_write(struct i2c_client *client, unsigned char adr, + unsigned char *data, unsigned char len) +{ + int ret = 0; + unsigned char _data[16]; + struct i2c_msg wr; + int i; + + if (!client || !data || len > 15) { + ret = -EINVAL; + goto done; + } + + _DBG(1, "client=%p, adr=%d, buf=%p, len=%d", client, adr, data, len); + + _data[0] = adr; + for (i = 0; i < len; i++) { + _data[i + 1] = data[i]; + _DBG(5, "data[%d] = 0x%02x (%d)", i, data[i], data[i]); + } + + wr.addr = client->addr; + wr.flags = 0; + wr.len = len + 1; + wr.buf = _data; + + ret = i2c_transfer(client->adapter, &wr, 1); + if (ret == 1) { + ret = 0; + } + +done: + return ret; +} + +static int rtc8564_attach(struct i2c_adapter *adap, int addr, int kind) +{ + int ret; + struct i2c_client *new_client; + struct rtc8564_data *d; + unsigned char data[10]; + unsigned char ad[1] = { 0 }; + struct i2c_msg ctrl_wr[1] = { + {addr, 0, 2, data} + }; + struct i2c_msg ctrl_rd[2] = { + {addr, 0, 1, ad}, + {addr, I2C_M_RD, 2, data} + }; + + d = kmalloc(sizeof(struct rtc8564_data), GFP_KERNEL); + if (!d) { + ret = -ENOMEM; + goto done; + } + memset(d, 0, sizeof(struct rtc8564_data)); + new_client = &d->client; + + strlcpy(new_client->name, "RTC8564", I2C_NAME_SIZE); + i2c_set_clientdata(new_client, d); + new_client->id = rtc8564_driver.id; + new_client->flags = I2C_CLIENT_ALLOW_USE | I2C_DF_NOTIFY; + new_client->addr = addr; + new_client->adapter = adap; + new_client->driver = &rtc8564_driver; + + _DBG(1, "client=%p", new_client); + _DBG(1, "client.id=%d", new_client->id); + + /* init ctrl1 reg */ + data[0] = 0; + data[1] = 0; + ret = i2c_transfer(new_client->adapter, ctrl_wr, 1); + if (ret != 1) { + printk(KERN_INFO "rtc8564: cant init ctrl1\n"); + ret = -ENODEV; + goto done; + } + + /* read back ctrl1 and ctrl2 */ + ret = i2c_transfer(new_client->adapter, ctrl_rd, 2); + if (ret != 2) { + printk(KERN_INFO "rtc8564: cant read ctrl\n"); + ret = -ENODEV; + goto done; + } + + d->ctrl = data[0] | (data[1] << 8); + + _DBG(1, "RTC8564_REG_CTRL1=%02x, RTC8564_REG_CTRL2=%02x", + data[0], data[1]); + + ret = i2c_attach_client(new_client); +done: + if (ret) { + kfree(d); + } + return ret; +} + +static int rtc8564_probe(struct i2c_adapter *adap) +{ + return i2c_probe(adap, &addr_data, rtc8564_attach); +} + +static int rtc8564_detach(struct i2c_client *client) +{ + i2c_detach_client(client); + kfree(i2c_get_clientdata(client)); + return 0; +} + +static int rtc8564_get_datetime(struct i2c_client *client, struct rtc_tm *dt) +{ + int ret = -EIO; + unsigned char buf[15]; + + _DBG(1, "client=%p, dt=%p", client, dt); + + if (!dt || !client) + return -EINVAL; + + memset(buf, 0, sizeof(buf)); + + ret = rtc8564_read(client, 0, buf, 15); + if (ret) + return ret; + + /* century stored in minute alarm reg */ + dt->year = BCD_TO_BIN(buf[RTC8564_REG_YEAR]); + dt->year += 100 * BCD_TO_BIN(buf[RTC8564_REG_AL_MIN] & 0x3f); + dt->mday = BCD_TO_BIN(buf[RTC8564_REG_DAY] & 0x3f); + dt->wday = BCD_TO_BIN(buf[RTC8564_REG_WDAY] & 7); + dt->mon = BCD_TO_BIN(buf[RTC8564_REG_MON_CENT] & 0x1f); + + dt->secs = BCD_TO_BIN(buf[RTC8564_REG_SEC] & 0x7f); + dt->vl = (buf[RTC8564_REG_SEC] & 0x80) == 0x80; + dt->mins = BCD_TO_BIN(buf[RTC8564_REG_MIN] & 0x7f); + dt->hours = BCD_TO_BIN(buf[RTC8564_REG_HR] & 0x3f); + + _DBGRTCTM(2, *dt); + + return 0; +} + +static int +rtc8564_set_datetime(struct i2c_client *client, struct rtc_tm *dt, int datetoo) +{ + int ret, len = 5; + unsigned char buf[15]; + + _DBG(1, "client=%p, dt=%p", client, dt); + + if (!dt || !client) + return -EINVAL; + + _DBGRTCTM(2, *dt); + + buf[RTC8564_REG_CTRL1] = CTRL1(client) | RTC8564_CTRL1_STOP; + buf[RTC8564_REG_CTRL2] = CTRL2(client); + buf[RTC8564_REG_SEC] = BIN_TO_BCD(dt->secs); + buf[RTC8564_REG_MIN] = BIN_TO_BCD(dt->mins); + buf[RTC8564_REG_HR] = BIN_TO_BCD(dt->hours); + + if (datetoo) { + len += 5; + buf[RTC8564_REG_DAY] = BIN_TO_BCD(dt->mday); + buf[RTC8564_REG_WDAY] = BIN_TO_BCD(dt->wday); + buf[RTC8564_REG_MON_CENT] = BIN_TO_BCD(dt->mon) & 0x1f; + /* century stored in minute alarm reg */ + buf[RTC8564_REG_YEAR] = BIN_TO_BCD(dt->year % 100); + buf[RTC8564_REG_AL_MIN] = BIN_TO_BCD(dt->year / 100); + } + + ret = rtc8564_write(client, 0, buf, len); + if (ret) { + _DBG(1, "error writing data! %d", ret); + } + + buf[RTC8564_REG_CTRL1] = CTRL1(client); + ret = rtc8564_write(client, 0, buf, 1); + if (ret) { + _DBG(1, "error writing data! %d", ret); + } + + return ret; +} + +static int rtc8564_get_ctrl(struct i2c_client *client, unsigned int *ctrl) +{ + struct rtc8564_data *data = i2c_get_clientdata(client); + + if (!ctrl || !client) + return -1; + + *ctrl = data->ctrl; + return 0; +} + +static int rtc8564_set_ctrl(struct i2c_client *client, unsigned int *ctrl) +{ + struct rtc8564_data *data = i2c_get_clientdata(client); + unsigned char buf[2]; + + if (!ctrl || !client) + return -1; + + buf[0] = *ctrl & 0xff; + buf[1] = (*ctrl & 0xff00) >> 8; + data->ctrl = *ctrl; + + return rtc8564_write(client, 0, buf, 2); +} + +static int rtc8564_read_mem(struct i2c_client *client, struct mem *mem) +{ + + if (!mem || !client) + return -EINVAL; + + return rtc8564_read(client, mem->loc, mem->data, mem->nr); +} + +static int rtc8564_write_mem(struct i2c_client *client, struct mem *mem) +{ + + if (!mem || !client) + return -EINVAL; + + return rtc8564_write(client, mem->loc, mem->data, mem->nr); +} + +static int +rtc8564_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + + _DBG(1, "cmd=%d", cmd); + + switch (cmd) { + case RTC_GETDATETIME: + return rtc8564_get_datetime(client, arg); + + case RTC_SETTIME: + return rtc8564_set_datetime(client, arg, 0); + + case RTC_SETDATETIME: + return rtc8564_set_datetime(client, arg, 1); + + case RTC_GETCTRL: + return rtc8564_get_ctrl(client, arg); + + case RTC_SETCTRL: + return rtc8564_set_ctrl(client, arg); + + case MEM_READ: + return rtc8564_read_mem(client, arg); + + case MEM_WRITE: + return rtc8564_write_mem(client, arg); + + default: + return -EINVAL; + } +} + +static struct i2c_driver rtc8564_driver = { + .owner = THIS_MODULE, + .name = "RTC8564", + .id = I2C_DRIVERID_RTC8564, + .flags = I2C_DF_NOTIFY, + .attach_adapter = rtc8564_probe, + .detach_client = rtc8564_detach, + .command = rtc8564_command +}; + +static __init int rtc8564_init(void) +{ + return i2c_add_driver(&rtc8564_driver); +} + +static __exit void rtc8564_exit(void) +{ + i2c_del_driver(&rtc8564_driver); +} + +MODULE_AUTHOR("Stefan Eletzhofer "); +MODULE_DESCRIPTION("EPSON RTC8564 Driver"); +MODULE_LICENSE("GPL"); + +module_init(rtc8564_init); +module_exit(rtc8564_exit); diff --git a/drivers/i2c/chips/rtc8564.h b/drivers/i2c/chips/rtc8564.h new file mode 100644 index 000000000..e5342d10b --- /dev/null +++ b/drivers/i2c/chips/rtc8564.h @@ -0,0 +1,78 @@ +/* + * linux/drivers/i2c/chips/rtc8564.h + * + * Copyright (C) 2002-2004 Stefan Eletzhofer + * + * based on linux/drivers/acron/char/pcf8583.h + * Copyright (C) 2000 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +struct rtc_tm { + unsigned char secs; + unsigned char mins; + unsigned char hours; + unsigned char mday; + unsigned char mon; + unsigned short year; /* xxxx 4 digits :) */ + unsigned char wday; + unsigned char vl; +}; + +struct mem { + unsigned int loc; + unsigned int nr; + unsigned char *data; +}; + +#define RTC_GETDATETIME 0 +#define RTC_SETTIME 1 +#define RTC_SETDATETIME 2 +#define RTC_GETCTRL 3 +#define RTC_SETCTRL 4 +#define MEM_READ 5 +#define MEM_WRITE 6 + +#define RTC8564_REG_CTRL1 0x0 /* T 0 S 0 | T 0 0 0 */ +#define RTC8564_REG_CTRL2 0x1 /* 0 0 0 TI/TP | AF TF AIE TIE */ +#define RTC8564_REG_SEC 0x2 /* VL 4 2 1 | 8 4 2 1 */ +#define RTC8564_REG_MIN 0x3 /* x 4 2 1 | 8 4 2 1 */ +#define RTC8564_REG_HR 0x4 /* x x 2 1 | 8 4 2 1 */ +#define RTC8564_REG_DAY 0x5 /* x x 2 1 | 8 4 2 1 */ +#define RTC8564_REG_WDAY 0x6 /* x x x x | x 4 2 1 */ +#define RTC8564_REG_MON_CENT 0x7 /* C x x 1 | 8 4 2 1 */ +#define RTC8564_REG_YEAR 0x8 /* 8 4 2 1 | 8 4 2 1 */ +#define RTC8564_REG_AL_MIN 0x9 /* AE 4 2 1 | 8 4 2 1 */ +#define RTC8564_REG_AL_HR 0xa /* AE 4 2 1 | 8 4 2 1 */ +#define RTC8564_REG_AL_DAY 0xb /* AE x 2 1 | 8 4 2 1 */ +#define RTC8564_REG_AL_WDAY 0xc /* AE x x x | x 4 2 1 */ +#define RTC8564_REG_CLKOUT 0xd /* FE x x x | x x FD1 FD0 */ +#define RTC8564_REG_TCTL 0xe /* TE x x x | x x FD1 FD0 */ +#define RTC8564_REG_TIMER 0xf /* 8 bit binary */ + +/* Control reg */ +#define RTC8564_CTRL1_TEST1 (1<<3) +#define RTC8564_CTRL1_STOP (1<<5) +#define RTC8564_CTRL1_TEST2 (1<<7) + +#define RTC8564_CTRL2_TIE (1<<0) +#define RTC8564_CTRL2_AIE (1<<1) +#define RTC8564_CTRL2_TF (1<<2) +#define RTC8564_CTRL2_AF (1<<3) +#define RTC8564_CTRL2_TI_TP (1<<4) + +/* CLKOUT frequencies */ +#define RTC8564_FD_32768HZ (0x0) +#define RTC8564_FD_1024HZ (0x1) +#define RTC8564_FD_32 (0x2) +#define RTC8564_FD_1HZ (0x3) + +/* Timer CTRL */ +#define RTC8564_TD_4096HZ (0x0) +#define RTC8564_TD_64HZ (0x1) +#define RTC8564_TD_1HZ (0x2) +#define RTC8564_TD_1_60HZ (0x3) + +#define I2C_DRIVERID_RTC8564 0xf000 diff --git a/drivers/ide/h8300/ide-h8300.c b/drivers/ide/h8300/ide-h8300.c new file mode 100644 index 000000000..fb91cb8bf --- /dev/null +++ b/drivers/ide/h8300/ide-h8300.c @@ -0,0 +1,119 @@ +/* + * drivers/ide/ide-h8300.c + * H8/300 generic IDE interface + */ + +#include +#include +#include + +#include +#include + +#define bswap(d) \ +({ \ + u16 r; \ + __asm__("mov.b %w1,r1h\n\t" \ + "mov.b %x1,r1l\n\t" \ + "mov.w r1,%0" \ + :"=r"(r) \ + :"r"(d) \ + :"er1"); \ + (r); \ +}) + +static void mm_outw(u16 d, unsigned long a) +{ + __asm__("mov.b %w0,r2h\n\t" + "mov.b %x0,r2l\n\t" + "mov.w r2,@%1" + : + :"r"(d),"r"(a) + :"er2"); +} + +static u16 mm_inw(unsigned long a) +{ + register u16 r __asm__("er0"); + __asm__("mov.w @%1,r2\n\t" + "mov.b r2l,%x0\n\t" + "mov.b r2h,%w0" + :"=r"(r) + :"r"(a) + :"er2"); + return r; +} + +static void mm_outsw(unsigned long addr, void *buf, u32 len) +{ + unsigned short *bp = (unsigned short *)buf; + for (; len > 0; len--, bp++) + *(volatile u16 *)addr = bswap(*bp); +} + +static void mm_insw(unsigned long addr, void *buf, u32 len) +{ + unsigned short *bp = (unsigned short *)buf; + for (; len > 0; len--, bp++) + *bp = bswap(*(volatile u16 *)addr); +} + +#define H8300_IDE_GAP (2) + +static inline void hw_setup(hw_regs_t *hw) +{ + int i; + + memset(hw, 0, sizeof(hw_regs_t)); + for (i = 0; i <= IDE_STATUS_OFFSET; i++) + hw->io_ports[i] = CONFIG_H8300_IDE_BASE + H8300_IDE_GAP*i; + hw->io_ports[IDE_CONTROL_OFFSET] = CONFIG_H8300_IDE_ALT; + hw->irq = EXT_IRQ0 + CONFIG_H8300_IDE_IRQ; + hw->dma = NO_DMA; + hw->chipset = ide_generic; +} + +static inline void hwif_setup(ide_hwif_t *hwif) +{ + default_hwif_iops(hwif); + + hwif->mmio = 2; + hwif->OUTW = mm_outw; + hwif->OUTSW = mm_outsw; + hwif->INW = mm_inw; + hwif->INSW = mm_insw; + hwif->OUTL = NULL; + hwif->INL = NULL; + hwif->OUTSL = NULL; + hwif->INSL = NULL; +} + +void __init h8300_ide_init(void) +{ + hw_regs_t hw; + ide_hwif_t *hwif; + int idx; + + if (!request_region(CONFIG_H8300_IDE_BASE, H8300_IDE_GAP*8, "ide-h8300")) + goto out_busy; + if (!request_region(CONFIG_H8300_IDE_ALT, H8300_IDE_GAP, "ide-h8300")) { + release_region(CONFIG_H8300_IDE_BASE, H8300_IDE_GAP*8); + goto out_busy; + } + + hw_setup(&hw); + + /* register if */ + idx = ide_register_hw(&hw, &hwif); + if (idx == -1) { + printk(KERN_ERR "ide-h8300: IDE I/F register failed\n"); + return; + } + + hwif_setup(hwif); + printk(KERN_INFO "ide%d: H8/300 generic IDE interface\n", idx); + return; + +out_busy: + printk(KERN_ERR "ide-h8300: IDE I/F resource already used.\n"); +} diff --git a/drivers/ide/ide-tcq.c b/drivers/ide/ide-tcq.c deleted file mode 100644 index 4284fba2d..000000000 --- a/drivers/ide/ide-tcq.c +++ /dev/null @@ -1,808 +0,0 @@ -/* - * Copyright (C) 2001, 2002 Jens Axboe - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * 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 - */ - -/* - * Support for the DMA queued protocol, which enables ATA disk drives to - * use tagged command queueing. - */ -#include -#include -#include -#include -#include - -#include -#include - -/* - * warning: it will be _very_ verbose if defined - */ -#undef IDE_TCQ_DEBUG - -#ifdef IDE_TCQ_DEBUG -#define TCQ_PRINTK printk -#else -#define TCQ_PRINTK(x...) -#endif - -/* - * use nIEN or not - */ -#undef IDE_TCQ_NIEN - -/* - * we are leaving the SERVICE interrupt alone, IBM drives have it - * on per default and it can't be turned off. Doesn't matter, this - * is the sane config. - */ -#undef IDE_TCQ_FIDDLE_SI - -/* - * bad drive blacklist, for drives that raport tcq capability but don't - * work reliably with the default config. initially from freebsd table. - */ -struct ide_tcq_blacklist { - char *model; - char works; - unsigned int max_sectors; -}; - -static struct ide_tcq_blacklist ide_tcq_blacklist[] = { - { - .model = "IBM-DTTA", - .works = 1, - .max_sectors = 128, - }, - { - .model = "IBM-DJNA", - .works = 0, - }, - { - .model = "WDC AC", - .works = 0, - }, - { - .model = NULL, - }, -}; - -ide_startstop_t ide_dmaq_intr(ide_drive_t *drive); -ide_startstop_t ide_service(ide_drive_t *drive); - -static struct ide_tcq_blacklist *ide_find_drive_blacklist(ide_drive_t *drive) -{ - struct ide_tcq_blacklist *itb; - int i = 0; - - do { - itb = &ide_tcq_blacklist[i]; - - if (!itb->model) - break; - - if (!strncmp(drive->id->model, itb->model, strlen(itb->model))) - return itb; - - i++; - } while (1); - - return NULL; -} - -static inline void drive_ctl_nien(ide_drive_t *drive, int set) -{ -#ifdef IDE_TCQ_NIEN - if (IDE_CONTROL_REG) { - int mask = set ? 0x02 : 0x00; - - hwif->OUTB(drive->ctl | mask, IDE_CONTROL_REG); - } -#endif -} - -static ide_startstop_t ide_tcq_nop_handler(ide_drive_t *drive) -{ - ide_task_t *args = HWGROUP(drive)->rq->special; - ide_hwif_t *hwif = HWIF(drive); - int auto_poll_check = 0; - u8 stat, err; - - if (args->tfRegister[IDE_FEATURE_OFFSET] & 0x01) - auto_poll_check = 1; - - local_irq_enable(); - - stat = hwif->INB(IDE_STATUS_REG); - err = hwif->INB(IDE_ERROR_REG); - ide_end_drive_cmd(drive, stat, err); - - /* - * do taskfile and check ABRT bit -- intelligent adapters will not - * pass NOP with sub-code 0x01 to device, so the command will not - * fail there - */ - if (auto_poll_check) { - if (!(args->tfRegister[IDE_FEATURE_OFFSET] & ABRT_ERR)) { - HWIF(drive)->auto_poll = 1; - printk("%s: NOP Auto-poll enabled\n",HWIF(drive)->name); - } - } - - kfree(args); - return ide_stopped; -} - -/* - * if we encounter _any_ error doing I/O to one of the tags, we must - * invalidate the pending queue. clear the software busy queue and requeue - * on the request queue for restart. issue a WIN_NOP to clear hardware queue - */ -static void ide_tcq_invalidate_queue(ide_drive_t *drive) -{ - ide_hwgroup_t *hwgroup = HWGROUP(drive); - request_queue_t *q = drive->queue; - struct request *rq; - unsigned long flags; - - printk("%s: invalidating tag queue (%d commands)\n", drive->name, ata_pending_commands(drive)); - - /* - * first kill timer and block queue - */ - spin_lock_irqsave(&ide_lock, flags); - - del_timer(&hwgroup->timer); - - if (HWIF(drive)->dma) - HWIF(drive)->ide_dma_end(drive); - - blk_queue_invalidate_tags(q); - - drive->using_tcq = 0; - drive->queue_depth = 1; - hwgroup->busy = 0; - hwgroup->handler = NULL; - - spin_unlock_irqrestore(&ide_lock, flags); - - /* - * now kill hardware queue with a NOP - */ - rq = &hwgroup->wrq; - ide_init_drive_cmd(rq); - rq->buffer = hwgroup->cmd_buf; - memset(rq->buffer, 0, sizeof(hwgroup->cmd_buf)); - rq->buffer[0] = WIN_NOP; - ide_do_drive_cmd(drive, rq, ide_preempt); -} - -void ide_tcq_intr_timeout(unsigned long data) -{ - ide_drive_t *drive = (ide_drive_t *) data; - ide_hwgroup_t *hwgroup = HWGROUP(drive); - ide_hwif_t *hwif = HWIF(drive); - unsigned long flags; - - printk(KERN_ERR "ide_tcq_intr_timeout: timeout waiting for %s interrupt\n", hwgroup->rq ? "completion" : "service"); - - spin_lock_irqsave(&ide_lock, flags); - - if (!hwgroup->busy) - printk(KERN_ERR "ide_tcq_intr_timeout: hwgroup not busy\n"); - if (hwgroup->handler == NULL) - printk(KERN_ERR "ide_tcq_intr_timeout: missing isr!\n"); - - hwgroup->busy = 1; - spin_unlock_irqrestore(&ide_lock, flags); - - /* - * if pending commands, try service before giving up - */ - if (ata_pending_commands(drive)) { - u8 stat = hwif->INB(IDE_STATUS_REG); - - if ((stat & SRV_STAT) && (ide_service(drive) == ide_started)) - return; - } - - if (drive) - ide_tcq_invalidate_queue(drive); -} - -void __ide_tcq_set_intr(ide_hwgroup_t *hwgroup, ide_handler_t *handler) -{ - /* - * always just bump the timer for now, the timeout handling will - * have to be changed to be per-command - */ - hwgroup->timer.function = ide_tcq_intr_timeout; - hwgroup->timer.data = (unsigned long) hwgroup->drive; - mod_timer(&hwgroup->timer, jiffies + 5 * HZ); - - hwgroup->handler = handler; -} - -void ide_tcq_set_intr(ide_hwgroup_t *hwgroup, ide_handler_t *handler) -{ - unsigned long flags; - - spin_lock_irqsave(&ide_lock, flags); - __ide_tcq_set_intr(hwgroup, handler); - spin_unlock_irqrestore(&ide_lock, flags); -} - -/* - * wait 400ns, then poll for busy_mask to clear from alt status - */ -#define IDE_TCQ_WAIT (10000) -int ide_tcq_wait_altstat(ide_drive_t *drive, byte *stat, byte busy_mask) -{ - ide_hwif_t *hwif = HWIF(drive); - int i = 0; - - udelay(1); - - do { - *stat = hwif->INB(IDE_ALTSTATUS_REG); - - if (!(*stat & busy_mask)) - break; - - if (unlikely(i++ > IDE_TCQ_WAIT)) - return 1; - - udelay(10); - } while (1); - - return 0; -} - -/* - * issue SERVICE command to drive -- drive must have been selected first, - * and it must have reported a need for service (status has SRV_STAT set) - * - * Also, nIEN must be set as not to need protection against ide_dmaq_intr - */ -ide_startstop_t ide_service(ide_drive_t *drive) -{ - ide_hwif_t *hwif = HWIF(drive); - unsigned long flags; - struct request *rq; - byte feat, stat; - int tag; - - TCQ_PRINTK("%s: started service\n", drive->name); - - /* - * could be called with IDE_DMA in-progress from invalidate - * handler, refuse to do anything - */ - if (hwif->dma) - return ide_stopped; - - /* - * need to select the right drive first... - */ - if (drive != HWGROUP(drive)->drive) { - SELECT_DRIVE(drive); - udelay(10); - } - - drive_ctl_nien(drive, 1); - - /* - * send SERVICE, wait 400ns, wait for BUSY_STAT to clear - */ - hwif->OUTB(WIN_QUEUED_SERVICE, IDE_COMMAND_REG); - - if (ide_tcq_wait_altstat(drive, &stat, BUSY_STAT)) { - printk(KERN_ERR "ide_service: BUSY clear took too long\n"); - ide_dump_status(drive, "ide_service", stat); - ide_tcq_invalidate_queue(drive); - return ide_stopped; - } - - drive_ctl_nien(drive, 0); - - /* - * FIXME, invalidate queue - */ - if (stat & ERR_STAT) { - ide_dump_status(drive, "ide_service", stat); - ide_tcq_invalidate_queue(drive); - return ide_stopped; - } - - /* - * should not happen, a buggy device could introduce loop - */ - feat = hwif->INB(IDE_NSECTOR_REG); - if (feat & REL) { - HWGROUP(drive)->rq = NULL; - printk(KERN_ERR "%s: release in service\n", drive->name); - return ide_stopped; - } - - tag = feat >> 3; - - TCQ_PRINTK("ide_service: stat %x, feat %x\n", stat, feat); - - spin_lock_irqsave(&ide_lock, flags); - - if ((rq = blk_queue_find_tag(drive->queue, tag))) { - HWGROUP(drive)->rq = rq; - - /* - * we'll start a dma read or write, device will trigger - * interrupt to indicate end of transfer, release is not - * allowed - */ - TCQ_PRINTK("ide_service: starting command, stat=%x\n", stat); - spin_unlock_irqrestore(&ide_lock, flags); - return __ide_dma_queued_start(drive); - } - - printk(KERN_ERR "ide_service: missing request for tag %d\n", tag); - spin_unlock_irqrestore(&ide_lock, flags); - return ide_stopped; -} - -ide_startstop_t ide_check_service(ide_drive_t *drive) -{ - ide_hwif_t *hwif = HWIF(drive); - byte stat; - - TCQ_PRINTK("%s: ide_check_service\n", drive->name); - - if (!ata_pending_commands(drive)) - return ide_stopped; - - stat = hwif->INB(IDE_STATUS_REG); - if (stat & SRV_STAT) - return ide_service(drive); - - /* - * we have pending commands, wait for interrupt - */ - TCQ_PRINTK("%s: wait for service interrupt\n", drive->name); - ide_tcq_set_intr(HWGROUP(drive), ide_dmaq_intr); - return ide_started; -} - -ide_startstop_t ide_dmaq_complete(ide_drive_t *drive, struct request *rq, byte stat) -{ - byte dma_stat; - - /* - * transfer was in progress, stop DMA engine - */ - dma_stat = HWIF(drive)->ide_dma_end(drive); - - /* - * must be end of I/O, check status and complete as necessary - */ - if (unlikely(!OK_STAT(stat, READY_STAT, drive->bad_wstat | DRQ_STAT))) { - printk(KERN_ERR "ide_dmaq_intr: %s: error status %x\n",drive->name,stat); - ide_dump_status(drive, "ide_dmaq_complete", stat); - ide_tcq_invalidate_queue(drive); - return ide_stopped; - } - - if (dma_stat) - printk(KERN_WARNING "%s: bad DMA status (dma_stat=%x)\n", drive->name, dma_stat); - - TCQ_PRINTK("ide_dmaq_complete: ending %p, tag %d\n", rq, rq->tag); - ide_end_request(drive, 1, rq->nr_sectors); - - /* - * we completed this command, check if we can service a new command - */ - return ide_check_service(drive); -} - -/* - * intr handler for queued dma operations. this can be entered for two - * reasons: - * - * 1) device has completed dma transfer - * 2) service request to start a command - * - * if the drive has an active tag, we first complete that request before - * processing any pending SERVICE. - */ -ide_startstop_t ide_dmaq_intr(ide_drive_t *drive) -{ - struct request *rq = HWGROUP(drive)->rq; - ide_hwif_t *hwif = HWIF(drive); - byte stat = hwif->INB(IDE_STATUS_REG); - - TCQ_PRINTK("ide_dmaq_intr: stat=%x\n", stat); - - /* - * if a command completion interrupt is pending, do that first and - * check service afterwards - */ - if (rq) { - TCQ_PRINTK("ide_dmaq_intr: completion\n"); - return ide_dmaq_complete(drive, rq, stat); - } - - /* - * service interrupt - */ - if (stat & SRV_STAT) { - TCQ_PRINTK("ide_dmaq_intr: SERV (stat=%x)\n", stat); - return ide_service(drive); - } - - printk("ide_dmaq_intr: stat=%x, not expected\n", stat); - return ide_check_service(drive); -} - -/* - * check if the ata adapter this drive is attached to supports the - * NOP auto-poll for multiple tcq enabled drives on one channel - */ -static int ide_tcq_check_autopoll(ide_drive_t *drive) -{ - ide_task_t *args; - int i, drives; - - /* - * only need to probe if both drives on a channel support tcq - */ - for (i = 0, drives = 0; i < MAX_DRIVES; i++) - if (HWIF(drive)->drives[i].present && drive->media == ide_disk) - drives++; - - if (drives <= 1) - return 0; - - /* - * what a mess... - */ - args = kmalloc(sizeof(*args), GFP_ATOMIC); - if (!args) - return 1; - - memset(args, 0, sizeof(*args)); - - args->tfRegister[IDE_FEATURE_OFFSET] = 0x01; - args->tfRegister[IDE_COMMAND_OFFSET] = WIN_NOP; - args->command_type = IDE_DRIVE_TASK_NO_DATA; - args->handler = ide_tcq_nop_handler; - return ide_raw_taskfile(drive, args, NULL); -} - -/* - * configure the drive for tcq - */ -static int ide_tcq_configure(ide_drive_t *drive) -{ - int tcq_mask = 1 << 1 | 1 << 14; - int tcq_bits = tcq_mask | 1 << 15; - ide_task_t *args; - - /* - * bit 14 and 1 must be set in word 83 of the device id to indicate - * support for dma queued protocol, and bit 15 must be cleared - */ - if ((drive->id->command_set_2 & tcq_bits) ^ tcq_mask) { - printk(KERN_INFO "%s: TCQ not supported\n", drive->name); - return -EIO; - } - - args = kmalloc(sizeof(*args), GFP_ATOMIC); - if (!args) - return -ENOMEM; - - memset(args, 0, sizeof(ide_task_t)); - args->tfRegister[IDE_COMMAND_OFFSET] = WIN_SETFEATURES; - args->tfRegister[IDE_FEATURE_OFFSET] = SETFEATURES_EN_WCACHE; - args->command_type = IDE_DRIVE_TASK_NO_DATA; - args->handler = &task_no_data_intr; - - if (ide_raw_taskfile(drive, args, NULL)) { - printk(KERN_WARNING "%s: failed to enable write cache\n", drive->name); - goto err; - } - - /* - * disable RELease interrupt, it's quicker to poll this after - * having sent the command opcode - */ - memset(args, 0, sizeof(ide_task_t)); - args->tfRegister[IDE_COMMAND_OFFSET] = WIN_SETFEATURES; - args->tfRegister[IDE_FEATURE_OFFSET] = SETFEATURES_DIS_RI; - args->command_type = IDE_DRIVE_TASK_NO_DATA; - args->handler = &task_no_data_intr; - - if (ide_raw_taskfile(drive, args, NULL)) { - printk(KERN_ERR "%s: disabling release interrupt fail\n", drive->name); - goto err; - } - -#ifdef IDE_TCQ_FIDDLE_SI - /* - * enable SERVICE interrupt - */ - memset(args, 0, sizeof(ide_task_t)); - args->tfRegister[IDE_COMMAND_OFFSET] = WIN_SETFEATURES; - args->tfRegister[IDE_FEATURE_OFFSET] = SETFEATURES_EN_SI; - args->command_type = IDE_DRIVE_TASK_NO_DATA; - args->handler = &task_no_data_intr; - - if (ide_raw_taskfile(drive, args, NULL)) { - printk(KERN_ERR "%s: enabling service interrupt fail\n", drive->name); - goto err; - } -#endif - - kfree(args); - return 0; -err: - kfree(args); - return -EIO; -} - -/* - * for now assume that command list is always as big as we need and don't - * attempt to shrink it on tcq disable - */ -static int ide_enable_queued(ide_drive_t *drive, int on) -{ - struct ide_tcq_blacklist *itb; - int depth = drive->using_tcq ? drive->queue_depth : 0; - - /* - * disable or adjust queue depth - */ - if (!on) { - if (drive->using_tcq) - printk(KERN_INFO "%s: TCQ disabled\n", drive->name); - - drive->using_tcq = 0; - return 0; - } - - if (ide_tcq_configure(drive)) { - drive->using_tcq = 0; - return 1; - } - - /* - * some drives need limited transfer size in tcq - */ - itb = ide_find_drive_blacklist(drive); - if (itb && itb->max_sectors) { - if (itb->max_sectors > HWIF(drive)->rqsize) - itb->max_sectors = HWIF(drive)->rqsize; - - blk_queue_max_sectors(drive->queue, itb->max_sectors); - } - - /* - * enable block tagging - */ - if (!blk_queue_tagged(drive->queue)) - blk_queue_init_tags(drive->queue, IDE_MAX_TAG, NULL); - - /* - * check auto-poll support - */ - ide_tcq_check_autopoll(drive); - - if (depth != drive->queue_depth) - printk(KERN_INFO "%s: tagged command queueing enabled, command queue depth %d\n", drive->name, drive->queue_depth); - - drive->using_tcq = 1; - return 0; -} - -int ide_tcq_wait_dataphase(ide_drive_t *drive) -{ - ide_hwif_t *hwif = HWIF(drive); - byte stat; - int i; - - do { - stat = hwif->INB(IDE_STATUS_REG); - if (!(stat & BUSY_STAT)) - break; - - udelay(10); - } while (1); - - if (OK_STAT(stat, READY_STAT | DRQ_STAT, drive->bad_wstat)) - return 0; - - i = 0; - udelay(1); - do { - stat = hwif->INB(IDE_STATUS_REG); - - if (OK_STAT(stat, READY_STAT | DRQ_STAT, drive->bad_wstat)) - break; - - ++i; - if (unlikely(i >= IDE_TCQ_WAIT)) - return 1; - - udelay(10); - } while (1); - - return 0; -} - -static int ide_tcq_check_blacklist(ide_drive_t *drive) -{ - struct ide_tcq_blacklist *itb = ide_find_drive_blacklist(drive); - - if (!itb) - return 0; - - return !itb->works; -} - -int __ide_dma_queued_on(ide_drive_t *drive) -{ - ide_hwif_t *hwif = HWIF(drive); - - if (drive->media != ide_disk) - return 1; - if (!drive->using_dma) - return 1; - if (hwif->chipset == ide_pdc4030) - return 1; - if (ide_tcq_check_blacklist(drive)) { - printk(KERN_WARNING "%s: tcq forbidden by blacklist\n", - drive->name); - return 1; - } - if (hwif->drives[0].present && hwif->drives[1].present) { - printk(KERN_WARNING "%s: only one drive on a channel supported" - " for tcq\n", drive->name); - return 1; - } - if (ata_pending_commands(drive)) { - printk(KERN_WARNING "ide-tcq; can't toggle tcq feature on " - "busy drive\n"); - return 1; - } - - return ide_enable_queued(drive, 1); -} - -int __ide_dma_queued_off(ide_drive_t *drive) -{ - if (drive->media != ide_disk) - return 1; - if (ata_pending_commands(drive)) { - printk("ide-tcq; can't toggle tcq feature on busy drive\n"); - return 1; - } - - return ide_enable_queued(drive, 0); -} - -static ide_startstop_t ide_dma_queued_rw(ide_drive_t *drive, u8 command) -{ - ide_hwif_t *hwif = HWIF(drive); - unsigned long flags; - byte stat, feat; - - TCQ_PRINTK("%s: starting tag\n", drive->name); - - /* - * set nIEN, tag start operation will enable again when - * it is safe - */ - drive_ctl_nien(drive, 1); - - TCQ_PRINTK("%s: sending cmd=%x\n", drive->name, command); - hwif->OUTB(command, IDE_COMMAND_REG); - - if (ide_tcq_wait_altstat(drive, &stat, BUSY_STAT)) { - printk("%s: alt stat timeout\n", drive->name); - goto err; - } - - drive_ctl_nien(drive, 0); - - if (stat & ERR_STAT) - goto err; - - /* - * bus not released, start dma - */ - feat = hwif->INB(IDE_NSECTOR_REG); - if (!(feat & REL)) { - TCQ_PRINTK("IMMED in queued_start, feat=%x\n", feat); - return __ide_dma_queued_start(drive); - } - - /* - * drive released the bus, clear active request and check for service - */ - spin_lock_irqsave(&ide_lock, flags); - HWGROUP(drive)->rq = NULL; - __ide_tcq_set_intr(HWGROUP(drive), ide_dmaq_intr); - spin_unlock_irqrestore(&ide_lock, flags); - - TCQ_PRINTK("REL in queued_start\n"); - - stat = hwif->INB(IDE_STATUS_REG); - if (stat & SRV_STAT) - return ide_service(drive); - - return ide_released; -err: - ide_dump_status(drive, "rw_queued", stat); - ide_tcq_invalidate_queue(drive); - return ide_stopped; -} - -ide_startstop_t __ide_dma_queued_read(ide_drive_t *drive) -{ - u8 command = WIN_READDMA_QUEUED; - - if (drive->addressing == 1) - command = WIN_READDMA_QUEUED_EXT; - - return ide_dma_queued_rw(drive, command); -} - -ide_startstop_t __ide_dma_queued_write(ide_drive_t *drive) -{ - u8 command = WIN_WRITEDMA_QUEUED; - - if (drive->addressing == 1) - command = WIN_WRITEDMA_QUEUED_EXT; - - return ide_dma_queued_rw(drive, command); -} - -ide_startstop_t __ide_dma_queued_start(ide_drive_t *drive) -{ - ide_hwgroup_t *hwgroup = HWGROUP(drive); - struct request *rq = hwgroup->rq; - ide_hwif_t *hwif = HWIF(drive); - unsigned int reading = 0; - - TCQ_PRINTK("ide_dma: setting up queued tag=%d\n", rq->tag); - - if (!hwgroup->busy) - printk(KERN_ERR "queued_rw: hwgroup not busy\n"); - - if (ide_tcq_wait_dataphase(drive)) { - printk(KERN_WARNING "timeout waiting for data phase\n"); - return ide_stopped; - } - - if (rq_data_dir(rq) == READ) - reading = 1 << 3; - - if (ide_start_dma(hwif, drive, reading)) - return ide_stopped; - - ide_tcq_set_intr(hwgroup, ide_dmaq_intr); - - if (!hwif->ide_dma_begin(drive)) - return ide_started; - - return ide_stopped; -} diff --git a/drivers/ide/pci/alim15x3.h b/drivers/ide/pci/alim15x3.h deleted file mode 100644 index 439bd5031..000000000 --- a/drivers/ide/pci/alim15x3.h +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef ALI15X3_H -#define ALI15X3_H - -#include -#include -#include - -#define DISPLAY_ALI_TIMINGS - -static unsigned int init_chipset_ali15x3(struct pci_dev *, const char *); -static void init_hwif_common_ali15x3(ide_hwif_t *); -static void init_hwif_ali15x3(ide_hwif_t *); -static void init_dma_ali15x3(ide_hwif_t *, unsigned long); - -static ide_pci_device_t ali15x3_chipsets[] __devinitdata = { - { /* 0 */ - .vendor = PCI_VENDOR_ID_AL, - .device = PCI_DEVICE_ID_AL_M5229, - .name = "ALI15X3", - .init_chipset = init_chipset_ali15x3, - .init_iops = NULL, - .init_hwif = init_hwif_ali15x3, - .init_dma = init_dma_ali15x3, - .channels = 2, - .autodma = AUTODMA, - .enablebits = {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, - .bootable = ON_BOARD, - .extra = 0 - },{ - .vendor = 0, - .device = 0, - .channels = 0, - .bootable = EOL, - } -}; - -#endif /* ALI15X3 */ diff --git a/drivers/ide/pci/amd74xx.h b/drivers/ide/pci/amd74xx.h deleted file mode 100644 index cb265c8b7..000000000 --- a/drivers/ide/pci/amd74xx.h +++ /dev/null @@ -1,168 +0,0 @@ -#ifndef AMD74XX_H -#define AMD74XX_H - -#include -#include -#include - -#define DISPLAY_AMD_TIMINGS - -static unsigned int init_chipset_amd74xx(struct pci_dev *, const char *); -static void init_hwif_amd74xx(ide_hwif_t *); - -static ide_pci_device_t amd74xx_chipsets[] __devinitdata = { - { /* 0 */ - .vendor = PCI_VENDOR_ID_AMD, - .device = PCI_DEVICE_ID_AMD_COBRA_7401, - .name = "AMD7401", - .init_chipset = init_chipset_amd74xx, - .init_hwif = init_hwif_amd74xx, - .channels = 2, - .autodma = AUTODMA, - .enablebits = {{0x40,0x02,0x02}, {0x40,0x01,0x01}}, - .bootable = ON_BOARD, - .extra = 0 - },{ /* 1 */ - .vendor = PCI_VENDOR_ID_AMD, - .device = PCI_DEVICE_ID_AMD_VIPER_7409, - .name = "AMD7409", - .init_chipset = init_chipset_amd74xx, - .init_hwif = init_hwif_amd74xx, - .channels = 2, - .autodma = AUTODMA, - .enablebits = {{0x40,0x02,0x02}, {0x40,0x01,0x01}}, - .bootable = ON_BOARD, - .extra = 0 - },{ /* 2 */ - .vendor = PCI_VENDOR_ID_AMD, - .device = PCI_DEVICE_ID_AMD_VIPER_7411, - .name = "AMD7411", - .init_chipset = init_chipset_amd74xx, - .init_hwif = init_hwif_amd74xx, - .channels = 2, - .autodma = AUTODMA, - .enablebits = {{0x40,0x02,0x02}, {0x40,0x01,0x01}}, - .bootable = ON_BOARD, - .extra = 0 - },{ /* 3 */ - .vendor = PCI_VENDOR_ID_AMD, - .device = PCI_DEVICE_ID_AMD_OPUS_7441, - .name = "AMD7441", - .init_chipset = init_chipset_amd74xx, - .init_hwif = init_hwif_amd74xx, - .channels = 2, - .autodma = AUTODMA, - .enablebits = {{0x40,0x02,0x02}, {0x40,0x01,0x01}}, - .bootable = ON_BOARD, - .extra = 0 - },{ /* 4 */ - .vendor = PCI_VENDOR_ID_AMD, - .device = PCI_DEVICE_ID_AMD_8111_IDE, - .name = "AMD8111", - .init_chipset = init_chipset_amd74xx, - .init_hwif = init_hwif_amd74xx, - .autodma = AUTODMA, - .channels = 2, - .enablebits = {{0x40,0x02,0x02}, {0x40,0x01,0x01}}, - .bootable = ON_BOARD, - .extra = 0 - }, - { /* 5 */ - .vendor = PCI_VENDOR_ID_NVIDIA, - .device = PCI_DEVICE_ID_NVIDIA_NFORCE_IDE, - .name = "NFORCE", - .init_chipset = init_chipset_amd74xx, - .init_hwif = init_hwif_amd74xx, - .channels = 2, - .autodma = AUTODMA, - .enablebits = {{0x50,0x02,0x02}, {0x50,0x01,0x01}}, - .bootable = ON_BOARD, - .extra = 0, - }, - { /* 6 */ - .vendor = PCI_VENDOR_ID_NVIDIA, - .device = PCI_DEVICE_ID_NVIDIA_NFORCE2_IDE, - .name = "NFORCE2", - .init_chipset = init_chipset_amd74xx, - .init_hwif = init_hwif_amd74xx, - .channels = 2, - .autodma = AUTODMA, - .enablebits = {{0x50,0x02,0x02}, {0x50,0x01,0x01}}, - .bootable = ON_BOARD, - .extra = 0, - }, - { /* 7 */ - .vendor = PCI_VENDOR_ID_NVIDIA, - .device = PCI_DEVICE_ID_NVIDIA_NFORCE2S_IDE, - .name = "NFORCE2S", - .init_chipset = init_chipset_amd74xx, - .init_hwif = init_hwif_amd74xx, - .channels = 2, - .autodma = AUTODMA, - .enablebits = {{0x50,0x02,0x02}, {0x50,0x01,0x01}}, - .bootable = ON_BOARD, - }, - { /* 8 */ - .vendor = PCI_VENDOR_ID_NVIDIA, - .device = PCI_DEVICE_ID_NVIDIA_NFORCE2S_SATA, - .name = "NFORCE2S-SATA", - .init_chipset = init_chipset_amd74xx, - .init_hwif = init_hwif_amd74xx, - .channels = 2, - .autodma = AUTODMA, - .enablebits = {{0x50,0x02,0x02}, {0x50,0x01,0x01}}, - .bootable = ON_BOARD, - }, - { /* 9 */ - .vendor = PCI_VENDOR_ID_NVIDIA, - .device = PCI_DEVICE_ID_NVIDIA_NFORCE3_IDE, - .name = "NFORCE3", - .init_chipset = init_chipset_amd74xx, - .init_hwif = init_hwif_amd74xx, - .channels = 2, - .autodma = AUTODMA, - .enablebits = {{0x50,0x02,0x02}, {0x50,0x01,0x01}}, - .bootable = ON_BOARD, - }, - { /* 10 */ - .vendor = PCI_VENDOR_ID_NVIDIA, - .device = PCI_DEVICE_ID_NVIDIA_NFORCE3S_IDE, - .name = "NFORCE3S", - .init_chipset = init_chipset_amd74xx, - .init_hwif = init_hwif_amd74xx, - .channels = 2, - .autodma = AUTODMA, - .enablebits = {{0x50,0x02,0x02}, {0x50,0x01,0x01}}, - .bootable = ON_BOARD, - }, - { /* 11 */ - .vendor = PCI_VENDOR_ID_NVIDIA, - .device = PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA, - .name = "NFORCE3S-SATA", - .init_chipset = init_chipset_amd74xx, - .init_hwif = init_hwif_amd74xx, - .channels = 2, - .autodma = AUTODMA, - .enablebits = {{0x50,0x02,0x02}, {0x50,0x01,0x01}}, - .bootable = ON_BOARD, - }, - { /* 12 */ - .vendor = PCI_VENDOR_ID_NVIDIA, - .device = PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA2, - .name = "NFORCE3S-SATA2", - .init_chipset = init_chipset_amd74xx, - .init_hwif = init_hwif_amd74xx, - .channels = 2, - .autodma = AUTODMA, - .enablebits = {{0x50,0x02,0x02}, {0x50,0x01,0x01}}, - .bootable = ON_BOARD, - }, - { - .vendor = 0, - .device = 0, - .channels = 0, - .bootable = EOL, - } -}; - -#endif /* AMD74XX_H */ diff --git a/drivers/ide/pci/cmd640.h b/drivers/ide/pci/cmd640.h deleted file mode 100644 index 28b6e0452..000000000 --- a/drivers/ide/pci/cmd640.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef CMD640_H -#define CMD640_H - -#include -#include -#include - -#define IDE_IGNORE ((void *)-1) - -static ide_pci_device_t cmd640_chipsets[] __initdata = { - { - .vendor = PCI_VENDOR_ID_CMD, - .device = PCI_DEVICE_ID_CMD_640, - .name = "CMD640", - .init_setup = NULL, - .init_chipset = NULL, - .init_iops = NULL, - .init_hwif = IDE_IGNORE, - .init_dma = NULL, - .channels = 2, - .autodma = NODMA, - .enablebits = {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, - .bootable = ON_BOARD, - .extra = 0 - },{ - .vendor = 0, - .device = 0, - .bootable = EOL, - } -} - -#endif /* CMD640_H */ diff --git a/drivers/ide/pci/cs5520.h b/drivers/ide/pci/cs5520.h deleted file mode 100644 index 21e8cb39d..000000000 --- a/drivers/ide/pci/cs5520.h +++ /dev/null @@ -1,48 +0,0 @@ -#ifndef CS5520_H -#define CS5520_H - -#include -#include -#include - -#define DISPLAY_CS5520_TIMINGS - -static unsigned int init_chipset_cs5520(struct pci_dev *, const char *); -static void init_hwif_cs5520(ide_hwif_t *); -static void cs5520_init_setup_dma(struct pci_dev *dev, struct ide_pci_device_s *d, ide_hwif_t *hwif); - -static ide_pci_device_t cyrix_chipsets[] __devinitdata = { - { - .vendor = PCI_VENDOR_ID_CYRIX, - .device = PCI_DEVICE_ID_CYRIX_5510, - .name = "Cyrix 5510", - .init_chipset = init_chipset_cs5520, - .init_setup_dma = cs5520_init_setup_dma, - .init_iops = NULL, - .init_hwif = init_hwif_cs5520, - .isa_ports = 1, - .channels = 2, - .autodma = AUTODMA, - .bootable = ON_BOARD, - .extra = 0, - }, - { - .vendor = PCI_VENDOR_ID_CYRIX, - .device = PCI_DEVICE_ID_CYRIX_5520, - .name = "Cyrix 5520", - .init_chipset = init_chipset_cs5520, - .init_setup_dma = cs5520_init_setup_dma, - .init_iops = NULL, - .init_hwif = init_hwif_cs5520, - .isa_ports = 1, - .channels = 2, - .autodma = AUTODMA, - .bootable = ON_BOARD, - .extra = 0, - } -}; - - -#endif /* CS5520_H */ - - diff --git a/drivers/ide/pci/cs5530.h b/drivers/ide/pci/cs5530.h deleted file mode 100644 index 89f448e98..000000000 --- a/drivers/ide/pci/cs5530.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef CS5530_H -#define CS5530_H - -#include -#include -#include - -#define DISPLAY_CS5530_TIMINGS - -static unsigned int init_chipset_cs5530(struct pci_dev *, const char *); -static void init_hwif_cs5530(ide_hwif_t *); - -static ide_pci_device_t cs5530_chipsets[] __devinitdata = { - { /* 0 */ - .vendor = PCI_VENDOR_ID_CYRIX, - .device = PCI_DEVICE_ID_CYRIX_5530_IDE, - .name = "CS5530", - .init_chipset = init_chipset_cs5530, - .init_iops = NULL, - .init_hwif = init_hwif_cs5530, - .channels = 2, - .autodma = AUTODMA, - .enablebits = {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, - .bootable = ON_BOARD, - .extra = 0, - },{ - .vendor = 0, - .device = 0, - .channels = 0, - .bootable = EOL, - } -}; - -#endif /* CS5530_H */ diff --git a/drivers/ide/pci/ns87415.h b/drivers/ide/pci/ns87415.h deleted file mode 100644 index 597803e40..000000000 --- a/drivers/ide/pci/ns87415.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef NS87415_H -#define NS87415_H - -#include -#include -#include - -static void init_hwif_ns87415(ide_hwif_t *); - -static ide_pci_device_t ns87415_chipsets[] __devinitdata = { - { /* 0 */ - .vendor = PCI_VENDOR_ID_NS, - .device = PCI_DEVICE_ID_NS_87415, - .name = "NS87415", - .init_chipset = NULL, - .init_iops = NULL, - .init_hwif = init_hwif_ns87415, - .channels = 2, - .autodma = AUTODMA, - .enablebits = {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, - .bootable = ON_BOARD, - .extra = 0, - },{ - .vendor = 0, - .device = 0, - .channels = 0, - .bootable = EOL, - } -}; - -#endif /* NS87415_H */ diff --git a/drivers/ide/pci/rz1000.h b/drivers/ide/pci/rz1000.h deleted file mode 100644 index 30823afe6..000000000 --- a/drivers/ide/pci/rz1000.h +++ /dev/null @@ -1,45 +0,0 @@ -#ifndef RZ100X_H -#define RZ100X_H - -#include -#include -#include - -static void init_hwif_rz1000(ide_hwif_t *); - -static ide_pci_device_t rz1000_chipsets[] __devinitdata = { -{ - .vendor = PCI_VENDOR_ID_PCTECH, - .device = PCI_DEVICE_ID_PCTECH_RZ1000, - .name = "RZ1000", - .init_chipset = NULL, - .init_iops = NULL, - .init_hwif = init_hwif_rz1000, - .init_dma = NULL, - .channels = 2, - .autodma = NODMA, - .enablebits = {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, - .bootable = ON_BOARD, - .extra = 0, - },{ - .vendor = PCI_VENDOR_ID_PCTECH, - .device = PCI_DEVICE_ID_PCTECH_RZ1001, - .name = "RZ1001", - .init_chipset = NULL, - .init_iops = NULL, - .init_hwif = init_hwif_rz1000, - .init_dma = NULL, - .channels = 2, - .autodma = NODMA, - .enablebits = {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, - .bootable = ON_BOARD, - .extra = 0, - },{ - .vendor = 0, - .device = 0, - .channels = 0, - .bootable = EOL, - } -}; - -#endif /* RZ100X_H */ diff --git a/drivers/ide/pci/sc1200.h b/drivers/ide/pci/sc1200.h deleted file mode 100644 index dc422a805..000000000 --- a/drivers/ide/pci/sc1200.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef SC1200_H -#define SC1200_H - -#include -#include -#include - -#define DISPLAY_SC1200_TIMINGS - -static unsigned int init_chipset_sc1200(struct pci_dev *, const char *); -static void init_hwif_sc1200(ide_hwif_t *); - -static ide_pci_device_t sc1200_chipsets[] __devinitdata = { - { /* 0 */ - .vendor = PCI_VENDOR_ID_NS, - .device = PCI_DEVICE_ID_NS_SCx200_IDE, - .name = "SC1200", - .init_chipset = init_chipset_sc1200, - .init_iops = NULL, - .init_hwif = init_hwif_sc1200, - .channels = 2, - .autodma = AUTODMA, - .enablebits = {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, - .bootable = ON_BOARD, - .extra = 0, - },{ - .vendor = 0, - .device = 0, - .channels = 0, - .bootable = EOL, - } -}; - -#endif /* SC1200_H */ diff --git a/drivers/ide/pci/siimage.h b/drivers/ide/pci/siimage.h deleted file mode 100644 index a47aae5b9..000000000 --- a/drivers/ide/pci/siimage.h +++ /dev/null @@ -1,73 +0,0 @@ -#ifndef SIIMAGE_H -#define SIIMAGE_H - -#include -#include -#include - -#include - -#define DISPLAY_SIIMAGE_TIMINGS - -#undef SIIMAGE_VIRTUAL_DMAPIO -#undef SIIMAGE_BUFFERED_TASKFILE -#undef SIIMAGE_LARGE_DMA - -#define SII_DEBUG 0 - -#if SII_DEBUG -#define siiprintk(x...) printk(x) -#else -#define siiprintk(x...) -#endif - -static unsigned int init_chipset_siimage(struct pci_dev *, const char *); -static void init_iops_siimage(ide_hwif_t *); -static void init_hwif_siimage(ide_hwif_t *); - -static ide_pci_device_t siimage_chipsets[] __devinitdata = { - { /* 0 */ - .vendor = PCI_VENDOR_ID_CMD, - .device = PCI_DEVICE_ID_SII_680, - .name = "SiI680", - .init_chipset = init_chipset_siimage, - .init_iops = init_iops_siimage, - .init_hwif = init_hwif_siimage, - .channels = 2, - .autodma = AUTODMA, - .enablebits = {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, - .bootable = ON_BOARD, - .extra = 0, - },{ /* 1 */ - .vendor = PCI_VENDOR_ID_CMD, - .device = PCI_DEVICE_ID_SII_3112, - .name = "SiI3112 Serial ATA", - .init_chipset = init_chipset_siimage, - .init_iops = init_iops_siimage, - .init_hwif = init_hwif_siimage, - .channels = 2, - .autodma = AUTODMA, - .enablebits = {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, - .bootable = ON_BOARD, - .extra = 0, - },{ /* 2 */ - .vendor = PCI_VENDOR_ID_CMD, - .device = PCI_DEVICE_ID_SII_1210SA, - .name = "Adaptec AAR-1210SA", - .init_chipset = init_chipset_siimage, - .init_iops = init_iops_siimage, - .init_hwif = init_hwif_siimage, - .channels = 2, - .autodma = AUTODMA, - .enablebits = {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, - .bootable = ON_BOARD, - .extra = 0, - },{ - .vendor = 0, - .device = 0, - .channels = 0, - .bootable = EOL, - } -}; - -#endif /* SIIMAGE_H */ diff --git a/drivers/ide/pci/sis5513.h b/drivers/ide/pci/sis5513.h deleted file mode 100644 index 79f3aa8b6..000000000 --- a/drivers/ide/pci/sis5513.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef SIS5513_H -#define SIS5513_H - -#include -#include -#include - -#define DISPLAY_SIS_TIMINGS - -static unsigned int init_chipset_sis5513(struct pci_dev *, const char *); -static void init_hwif_sis5513(ide_hwif_t *); - -static ide_pci_device_t sis5513_chipsets[] __devinitdata = { - { /* 0 */ - .vendor = PCI_VENDOR_ID_SI, - .device = PCI_DEVICE_ID_SI_5513, - .name = "SIS5513", - .init_chipset = init_chipset_sis5513, - .init_iops = NULL, - .init_hwif = init_hwif_sis5513, - .channels = 2, - .autodma = NOAUTODMA, - .enablebits = {{0x4a,0x02,0x02}, {0x4a,0x04,0x04}}, - .bootable = ON_BOARD, - .extra = 0 - },{ - .vendor = 0, - .device = 0, - .channels = 0, - .bootable = EOL, - } -}; - -#endif /* SIS5513_H */ diff --git a/drivers/ide/pci/sl82c105.h b/drivers/ide/pci/sl82c105.h deleted file mode 100644 index f71ee701d..000000000 --- a/drivers/ide/pci/sl82c105.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef W82C105_H -#define W82C105_H - -#include -#include -#include - -static unsigned int init_chipset_sl82c105(struct pci_dev *, const char *); -static void init_hwif_sl82c105(ide_hwif_t *); -static void init_dma_sl82c105(ide_hwif_t *, unsigned long); - -static ide_pci_device_t sl82c105_chipsets[] __devinitdata = { - { /* 0 */ - .vendor = PCI_VENDOR_ID_WINBOND, - .device = PCI_DEVICE_ID_WINBOND_82C105, - .name = "W82C105", - .init_chipset = init_chipset_sl82c105, - .init_iops = NULL, - .init_hwif = init_hwif_sl82c105, - .init_dma = init_dma_sl82c105, - .channels = 2, - .autodma = NOAUTODMA, - .enablebits = {{0x40,0x01,0x01}, {0x40,0x10,0x10}}, - .bootable = ON_BOARD, - .extra = 0, - },{ - .vendor = 0, - .device = 0, - .channels = 0, - .bootable = EOL, - } -}; - -#endif /* W82C105_H */ diff --git a/drivers/ide/pci/slc90e66.h b/drivers/ide/pci/slc90e66.h deleted file mode 100644 index 2f15858cd..000000000 --- a/drivers/ide/pci/slc90e66.h +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef SLC90E66_H -#define SLC90E66_H - -#include -#include -#include - -#define DISPLAY_SLC90E66_TIMINGS - -#define SLC90E66_DEBUG_DRIVE_INFO 0 - -static unsigned int init_chipset_slc90e66(struct pci_dev *, const char *); -static void init_hwif_slc90e66(ide_hwif_t *); - -static ide_pci_device_t slc90e66_chipsets[] __devinitdata = { - { /* 0 */ - .vendor = PCI_VENDOR_ID_EFAR, - .device = PCI_DEVICE_ID_EFAR_SLC90E66_1, - .name = "SLC90E66", - .init_chipset = init_chipset_slc90e66, - .init_iops = NULL, - .init_hwif = init_hwif_slc90e66, - .channels = 2, - .autodma = AUTODMA, - .enablebits = {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, - .bootable = ON_BOARD, - .extra = 0, - },{ - .vendor = 0, - .device = 0, - .channels = 0, - .bootable = EOL, - } -}; - -#endif /* SLC90E66_H */ diff --git a/drivers/ide/pci/triflex.h b/drivers/ide/pci/triflex.h deleted file mode 100644 index 7f6f73783..000000000 --- a/drivers/ide/pci/triflex.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * triflex.h - * - * Copyright (C) 2002 Hewlett-Packard Development Group, L.P. - * Author: Torben Mathiasen - * - */ -#ifndef TRIFLEX_H -#define TRIFLEX_H - -#include -#include -#include - -static unsigned int __devinit init_chipset_triflex(struct pci_dev *, const char *); -static void init_hwif_triflex(ide_hwif_t *); - -static ide_pci_device_t triflex_devices[] __devinitdata = { - { - .vendor = PCI_VENDOR_ID_COMPAQ, - .device = PCI_DEVICE_ID_COMPAQ_TRIFLEX_IDE, - .name = "TRIFLEX", - .init_chipset = init_chipset_triflex, - .init_iops = NULL, - .init_hwif = init_hwif_triflex, - .channels = 2, - .autodma = AUTODMA, - .enablebits = {{0x80, 0x01, 0x01}, {0x80, 0x02, 0x02}}, - .bootable = ON_BOARD, - },{ - .bootable = EOL, - } -}; - -static struct pci_device_id triflex_pci_tbl[] = { - { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_COMPAQ_TRIFLEX_IDE, PCI_ANY_ID, - PCI_ANY_ID, 0, 0, 0 }, - { 0, }, -}; -MODULE_DEVICE_TABLE(pci, triflex_pci_tbl); - -#endif /* TRIFLEX_H */ diff --git a/drivers/ide/pci/trm290.h b/drivers/ide/pci/trm290.h deleted file mode 100644 index e18b29e03..000000000 --- a/drivers/ide/pci/trm290.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef TRM290_H -#define TRM290_H - -#include -#include -#include - -extern void init_hwif_trm290(ide_hwif_t *); - -static ide_pci_device_t trm290_chipsets[] __devinitdata = { - { /* 0 */ - .vendor = PCI_VENDOR_ID_TEKRAM, - .device = PCI_DEVICE_ID_TEKRAM_DC290, - .name = "TRM290", - .init_chipset = NULL, - .init_iops = NULL, - .init_hwif = init_hwif_trm290, - .init_dma = NULL, - .channels = 2, - .autodma = NOAUTODMA, - .enablebits = {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, - .bootable = ON_BOARD, - .extra = 0, - },{ - .vendor = 0, - .device = 0, - .channels = 0, - .bootable = EOL, - } -}; - -#endif /* TRM290_H */ diff --git a/drivers/ide/pci/via82cxxx.h b/drivers/ide/pci/via82cxxx.h deleted file mode 100644 index a47f040e8..000000000 --- a/drivers/ide/pci/via82cxxx.h +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef VIA82CXXX_H -#define VIA82CXXX_H - -#include -#include -#include - -#define DISPLAY_VIA_TIMINGS - -static unsigned int init_chipset_via82cxxx(struct pci_dev *, const char *); -static void init_hwif_via82cxxx(ide_hwif_t *); - -static ide_pci_device_t via82cxxx_chipsets[] __devinitdata = { - { /* 0 */ - .vendor = PCI_VENDOR_ID_VIA, - .device = PCI_DEVICE_ID_VIA_82C576_1, - .name = "VP_IDE", - .init_chipset = init_chipset_via82cxxx, - .init_hwif = init_hwif_via82cxxx, - .channels = 2, - .autodma = NOAUTODMA, - .enablebits = {{0x40,0x02,0x02}, {0x40,0x01,0x01}}, - .bootable = ON_BOARD, - .extra = 0, - },{ /* 1 */ - .vendor = PCI_VENDOR_ID_VIA, - .device = PCI_DEVICE_ID_VIA_82C586_1, - .name = "VP_IDE", - .init_chipset = init_chipset_via82cxxx, - .init_hwif = init_hwif_via82cxxx, - .channels = 2, - .autodma = NOAUTODMA, - .enablebits = {{0x40,0x02,0x02}, {0x40,0x01,0x01}}, - .bootable = ON_BOARD, - .extra = 0, - },{ - .vendor = 0, - .device = 0, - .channels = 0, - .bootable = EOL, - } -}; - -#endif /* VIA82CXXX_H */ diff --git a/drivers/ide/ppc/swarm.c b/drivers/ide/ppc/swarm.c deleted file mode 100644 index d54a55525..000000000 --- a/drivers/ide/ppc/swarm.c +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (C) 2001 Broadcom Corporation - * - * 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. - */ - -/* Derived loosely from ide-pmac.c, so: - * - * Copyright (C) 1998 Paul Mackerras. - * Copyright (C) 1995-1998 Mark Lord - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define __IDE_SWARM_C - -#include - -void __init swarm_ide_probe(void) -{ - int i; - ide_hwif_t *hwif; - /* - * Find the first untaken slot in hwifs - */ - for (i = 0; i < MAX_HWIFS; i++) { - if (!ide_hwifs[i].io_ports[IDE_DATA_OFFSET]) { - break; - } - } - if (i == MAX_HWIFS) { - printk("No space for SWARM onboard IDE driver in ide_hwifs[]. Not enabled.\n"); - return; - } - - /* Set up our stuff */ - hwif = &ide_hwifs[i]; - hwif->hw.io_ports[IDE_DATA_OFFSET] = SWARM_IDE_REG(0x1f0); - hwif->hw.io_ports[IDE_ERROR_OFFSET] = SWARM_IDE_REG(0x1f1); - hwif->hw.io_ports[IDE_NSECTOR_OFFSET] = SWARM_IDE_REG(0x1f2); - hwif->hw.io_ports[IDE_SECTOR_OFFSET] = SWARM_IDE_REG(0x1f3); - hwif->hw.io_ports[IDE_LCYL_OFFSET] = SWARM_IDE_REG(0x1f4); - hwif->hw.io_ports[IDE_HCYL_OFFSET] = SWARM_IDE_REG(0x1f5); - hwif->hw.io_ports[IDE_SELECT_OFFSET] = SWARM_IDE_REG(0x1f6); - hwif->hw.io_ports[IDE_STATUS_OFFSET] = SWARM_IDE_REG(0x1f7); - hwif->hw.io_ports[IDE_CONTROL_OFFSET] = SWARM_IDE_REG(0x3f6); - hwif->hw.io_ports[IDE_IRQ_OFFSET] = SWARM_IDE_REG(0x3f7); -// hwif->hw->ack_intr = swarm_ide_ack_intr; - hwif->hw.irq = SWARM_IDE_INT; -#if 0 - hwif->iops = swarm_iops; -#else - hwif->OUTB = hwif->OUTBP = swarm_outb; - hwif->OUTW = hwif->OUTWP = swarm_outw; - hwif->OUTL = hwif->OUTLP = swarm_outl; - hwif->OUTSW = hwif->OUTSWP = swarm_outsw; - hwif->OUTSL = hwif->OUTSLP = swarm_outsl; - hwif->INB = hwif->INBP = swarm_inb; - hwif->INW = hwif->INWP = swarm_inw; - hwif->INL = hwif->INLP = swarm_inl; - hwif->INSW = hwif->INSWP = swarm_insw; - hwif->INSL = hwif->INSLP = swarm_insl; -#endif -#if 0 - hwif->pioops = swarm_pio_ops; -#else - hwif->ata_input_data = swarm_ata_input_data; - hwif->ata_output_data = swarm_ata_output_data; - hwif->atapi_input_bytes = swarm_atapi_input_bytes; - hwif->atapi_output_bytes = swarm_atapi_output_bytes; -#endif - memcpy(hwif->io_ports, hwif->hw.io_ports, sizeof(hwif->io_ports)); - hwif->irq = hwif->hw.irq; - printk("SWARM onboard IDE configured as device %i\n", i); - -#ifndef HWIF_PROBE_CLASSIC_METHOD - probe_hwif_init(hwif->index); -#endif /* HWIF_PROBE_CLASSIC_METHOD */ - -} - diff --git a/drivers/mtd/maps/ixp4xx.c b/drivers/mtd/maps/ixp4xx.c new file mode 100644 index 000000000..a10f92126 --- /dev/null +++ b/drivers/mtd/maps/ixp4xx.c @@ -0,0 +1,244 @@ +/* + * $Id: ixp4xx.c,v 1.1 2004/05/13 22:21:26 dsaxena Exp $ + * + * drivers/mtd/maps/ixp4xx.c + * + * MTD Map file for IXP4XX based systems. Please do not make per-board + * changes in here. If your board needs special setup, do it in your + * platform level code in arch/arm/mach-ixp4xx/board-setup.c + * + * Original Author: Intel Corporation + * Maintainer: Deepak Saxena + * + * Copyright (C) 2002 Intel Corporation + * Copyright (C) 2003-2004 MontaVista Software, Inc. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifndef __ARMEB__ +#define BYTE0(h) ((h) & 0xFF) +#define BYTE1(h) (((h) >> 8) & 0xFF) +#else +#define BYTE0(h) (((h) >> 8) & 0xFF) +#define BYTE1(h) ((h) & 0xFF) +#endif + +static __u16 +ixp4xx_read16(struct map_info *map, unsigned long ofs) +{ + return *(__u16 *) (map->map_priv_1 + ofs); +} + +/* + * The IXP4xx expansion bus only allows 16-bit wide acceses + * when attached to a 16-bit wide device (such as the 28F128J3A), + * so we can't just memcpy_fromio(). + */ +static void +ixp4xx_copy_from(struct map_info *map, void *to, + unsigned long from, ssize_t len) +{ + int i; + u8 *dest = (u8 *) to; + u16 *src = (u16 *) (map->map_priv_1 + from); + u16 data; + + for (i = 0; i < (len / 2); i++) { + data = src[i]; + dest[i * 2] = BYTE0(data); + dest[i * 2 + 1] = BYTE1(data); + } + + if (len & 1) + dest[len - 1] = BYTE0(src[i]); +} + +static void +ixp4xx_write16(struct map_info *map, __u16 d, unsigned long adr) +{ + *(__u16 *) (map->map_priv_1 + adr) = d; +} + +struct ixp4xx_flash_info { + struct mtd_info *mtd; + struct map_info map; + struct mtd_partition *partitions; + struct resource *res; +}; + +static const char *probes[] = { "RedBoot", "cmdlinepart", NULL }; + +static int +ixp4xx_flash_remove(struct device *_dev) +{ + struct platform_device *dev = to_platform_device(_dev); + struct flash_platform_data *plat = dev->dev.platform_data; + struct ixp4xx_flash_info *info = dev_get_drvdata(&dev->dev); + + dev_set_drvdata(&dev->dev, NULL); + + if(!info) + return 0; + + /* + * This is required for a soft reboot to work. + */ + ixp4xx_write16(&info->map, 0xff, 0x55 * 0x2); + + if (info->mtd) { + del_mtd_partitions(info->mtd); + map_destroy(info->mtd); + } + if (info->map.map_priv_1) + iounmap((void *) info->map.map_priv_1); + + if (info->partitions) + kfree(info->partitions); + + if (info->res) { + release_resource(info->res); + kfree(info->res); + } + + if (plat->exit) + plat->exit(); + + /* Disable flash write */ + *IXP4XX_EXP_CS0 &= ~IXP4XX_FLASH_WRITABLE; + + return 0; +} + +static int ixp4xx_flash_probe(struct device *_dev) +{ + struct platform_device *dev = to_platform_device(_dev); + struct flash_platform_data *plat = dev->dev.platform_data; + struct ixp4xx_flash_info *info; + int err = -1; + + if (!plat) + return -ENODEV; + + if (plat->init) { + err = plat->init(); + if (err) + return err; + } + + info = kmalloc(sizeof(struct ixp4xx_flash_info), GFP_KERNEL); + if(!info) { + err = -ENOMEM; + goto Error; + } + memzero(info, sizeof(struct ixp4xx_flash_info)); + + dev_set_drvdata(&dev->dev, info); + + /* + * Enable flash write + * TODO: Move this out to board specific code + */ + *IXP4XX_EXP_CS0 |= IXP4XX_FLASH_WRITABLE; + + /* + * Tell the MTD layer we're not 1:1 mapped so that it does + * not attempt to do a direct access on us. + */ + info->map.phys = NO_XIP; + info->map.size = dev->resource->end - dev->resource->start + 1; + + /* + * We only support 16-bit accesses for now. If and when + * any board use 8-bit access, we'll fixup the driver to + * handle that. + */ + info->map.buswidth = 2; + info->map.name = dev->dev.bus_id; + info->map.read16 = ixp4xx_read16, + info->map.write16 = ixp4xx_write16, + info->map.copy_from = ixp4xx_copy_from, + + info->res = request_mem_region(dev->resource->start, + dev->resource->end - dev->resource->start + 1, + "IXP4XXFlash"); + if (!info->res) { + printk(KERN_ERR "IXP4XXFlash: Could not reserve memory region\n"); + err = -ENOMEM; + goto Error; + } + + info->map.map_priv_1 = + (unsigned long) ioremap(dev->resource->start, + dev->resource->end - dev->resource->start + 1); + if (!info->map.map_priv_1) { + printk(KERN_ERR "IXP4XXFlash: Failed to ioremap region\n"); + err = -EIO; + goto Error; + } + + info->mtd = do_map_probe(plat->map_name, &info->map); + if (!info->mtd) { + printk(KERN_ERR "IXP4XXFlash: map_probe failed\n"); + err = -ENXIO; + goto Error; + } + info->mtd->owner = THIS_MODULE; + + err = parse_mtd_partitions(info->mtd, probes, &info->partitions, 0); + if (err > 0) { + err = add_mtd_partitions(info->mtd, info->partitions, err); + if(err) + printk(KERN_ERR "Could not parse partitions\n"); + } + + if (err) + goto Error; + + return 0; + +Error: + ixp4xx_flash_remove(_dev); + return err; +} + +static struct device_driver ixp4xx_flash_driver = { + .name = "IXP4XX-Flash", + .bus = &platform_bus_type, + .probe = ixp4xx_flash_probe, + .remove = ixp4xx_flash_remove, +}; + +static int __init ixp4xx_flash_init(void) +{ + return driver_register(&ixp4xx_flash_driver); +} + +static void __exit ixp4xx_flash_exit(void) +{ + driver_unregister(&ixp4xx_flash_driver); +} + + +module_init(ixp4xx_flash_init); +module_exit(ixp4xx_flash_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("MTD map driver for Intel IXP4xx systems") +MODULE_AUTHOR("Deepak Saxena"); + diff --git a/drivers/mtd/maps/wr_sbc82xx_flash.c b/drivers/mtd/maps/wr_sbc82xx_flash.c new file mode 100644 index 000000000..1901302d4 --- /dev/null +++ b/drivers/mtd/maps/wr_sbc82xx_flash.c @@ -0,0 +1,167 @@ +/* + * $Id: wr_sbc82xx_flash.c,v 1.1 2004/06/07 10:21:32 dwmw2 Exp $ + * + * Map for flash chips on Wind River PowerQUICC II SBC82xx board. + * + * Copyright (C) 2004 Red Hat, Inc. + * + * Author: David Woodhouse + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static struct mtd_info *sbcmtd[3]; +static struct mtd_partition *sbcmtd_parts[3]; + +struct map_info sbc82xx_flash_map[3] = { + {.name = "Boot flash"}, + {.name = "Alternate boot flash"}, + {.name = "User flash"} +}; + +static struct mtd_partition smallflash_parts[] = { + { + .name = "space", + .size = 0x100000, + .offset = 0, + }, { + .name = "bootloader", + .size = MTDPART_SIZ_FULL, + .offset = MTDPART_OFS_APPEND, + } +}; + +static struct mtd_partition bigflash_parts[] = { + { + .name = "bootloader", + .size = 0x80000, + .offset = 0, + }, { + .name = "file system", + .size = MTDPART_SIZ_FULL, + .offset = MTDPART_OFS_APPEND, + } +}; + +static const char *part_probes[] __initdata = {"cmdlinepart", "RedBoot", NULL}; + +int __init init_sbc82xx_flash(void) +{ + volatile memctl8260_t *mc = &immr->im_memctl; + int bigflash; + int i; + + /* First, register the boot flash, whichever we're booting from */ + if ((mc->memc_br0 & 0x00001800) == 0x00001800) { + bigflash = 0; + } else if ((mc->memc_br0 & 0x00001800) == 0x00000800) { + bigflash = 1; + } else { + printk(KERN_WARNING "Bus Controller register BR0 is %08x. Cannot determine flash configuration\n", mc->memc_br0); + return 1; + } + + /* Set parameters for the big flash chip (CS6 or CS0) */ + sbc82xx_flash_map[bigflash].buswidth = 4; + sbc82xx_flash_map[bigflash].size = 0x4000000; + + /* Set parameters for the small flash chip (CS0 or CS6) */ + sbc82xx_flash_map[!bigflash].buswidth = 1; + sbc82xx_flash_map[!bigflash].size = 0x200000; + + /* Set parameters for the user flash chip (CS1) */ + sbc82xx_flash_map[2].buswidth = 4; + sbc82xx_flash_map[2].size = 0x4000000; + + sbc82xx_flash_map[0].phys = mc->memc_br0 & 0xffff8000; + sbc82xx_flash_map[1].phys = mc->memc_br6 & 0xffff8000; + sbc82xx_flash_map[2].phys = mc->memc_br1 & 0xffff8000; + + for (i=0; i<3; i++) { + int8_t flashcs[3] = { 0, 6, 1 }; + int nr_parts; + + printk(KERN_NOTICE "PowerQUICC II %s (%ld MiB on CS%d", + sbc82xx_flash_map[i].name, sbc82xx_flash_map[i].size >> 20, flashcs[i]); + if (!sbc82xx_flash_map[i].phys) { + /* We know it can't be at zero. */ + printk("): disabled by bootloader.\n"); + continue; + } + printk(" at %08lx)\n", sbc82xx_flash_map[i].phys); + + sbc82xx_flash_map[i].virt = (unsigned long)ioremap(sbc82xx_flash_map[i].phys, sbc82xx_flash_map[i].size); + + if (!sbc82xx_flash_map[i].virt) { + printk("Failed to ioremap\n"); + continue; + } + + simple_map_init(&sbc82xx_flash_map[i]); + + sbcmtd[i] = do_map_probe("cfi_probe", &sbc82xx_flash_map[i]); + + if (!sbcmtd[i]) + continue; + + sbcmtd[i]->owner = THIS_MODULE; + + nr_parts = parse_mtd_partitions(sbcmtd[i], part_probes, + &sbcmtd_parts[i], 0); + if (nr_parts > 0) { + add_mtd_partitions (sbcmtd[i], sbcmtd_parts[i], nr_parts); + continue; + } + + /* No partitioning detected. Use default */ + if (i == 2) { + add_mtd_device(sbcmtd[i]); + } else if (i == bigflash) { + add_mtd_partitions (sbcmtd[i], bigflash_parts, ARRAY_SIZE(bigflash_parts)); + } else { + add_mtd_partitions (sbcmtd[i], smallflash_parts, ARRAY_SIZE(smallflash_parts)); + } + } + return 0; +} + +static void __exit cleanup_sbc82xx_flash(void) +{ + int i; + + for (i=0; i<3; i++) { + if (!sbcmtd[i]) + continue; + + if (i<2 || sbcmtd_parts[i]) + del_mtd_partitions(sbcmtd[i]); + else + del_mtd_device(sbcmtd[i]); + + kfree(sbcmtd_parts[i]); + map_destroy(sbcmtd[i]); + + iounmap((void *)sbc82xx_flash_map[i].virt); + sbc82xx_flash_map[i].virt = 0; + } +} + +module_init(init_sbc82xx_flash); +module_exit(cleanup_sbc82xx_flash); + + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("David Woodhouse "); +MODULE_DESCRIPTION("Flash map driver for WindRiver PowerQUICC II"); diff --git a/drivers/net/auto_irq.c b/drivers/net/auto_irq.c deleted file mode 100644 index 96ddc77b7..000000000 --- a/drivers/net/auto_irq.c +++ /dev/null @@ -1,68 +0,0 @@ -/* auto_irq.c: Auto-configure IRQ lines for linux. */ -/* - Written 1994 by Donald Becker. - - The author may be reached as becker@scyld.com - - This code is a general-purpose IRQ line detector for devices with - jumpered IRQ lines. If you can make the device raise an IRQ (and - that IRQ line isn't already being used), these routines will tell - you what IRQ line it's using -- perfect for those oh-so-cool boot-time - device probes! - - To use this, first call autoirq_setup(timeout). TIMEOUT is how many - 'jiffies' (1/100 sec.) to detect other devices that have active IRQ lines, - and can usually be zero at boot. 'autoirq_setup()' returns the bit - vector of nominally-available IRQ lines (lines may be physically in-use, - but not yet registered to a device). - Next, set up your device to trigger an interrupt. - Finally call autoirq_report(TIMEOUT) to find out which IRQ line was - most recently active. The TIMEOUT should usually be zero, but may - be set to the number of jiffies to wait for a slow device to raise an IRQ. - - The idea of using the setup timeout to filter out bogus IRQs came from - the serial driver. -*/ - - -#ifdef version -static const char *version= -"auto_irq.c:v1.11 Donald Becker (becker@scyld.com)"; -#endif - -#include -#include -#include -#include -#include -#include -#include - -static unsigned long irqs; - -void autoirq_setup(int waittime) -{ - irqs = probe_irq_on(); -} - -#define BUSY_LOOP_UNTIL(j) while ((long)(jiffies-(j)) < 0) ; -int autoirq_report(int waittime) -{ - unsigned long delay = jiffies + waittime; - BUSY_LOOP_UNTIL(delay) - return probe_irq_off(irqs); -} - -EXPORT_SYMBOL(autoirq_setup); -EXPORT_SYMBOL(autoirq_report); - - -/* - * Local variables: - * compile-command: "gcc -DKERNEL -Wall -O6 -fomit-frame-pointer -I/usr/src/linux/net/tcp -c auto_irq.c" - * version-control: t - * kept-new-versions: 5 - * c-indent-level: 4 - * tab-width: 4 - * End: - */ diff --git a/drivers/net/ibm_emac/ibm_emac.h b/drivers/net/ibm_emac/ibm_emac.h new file mode 100644 index 000000000..5310033ad --- /dev/null +++ b/drivers/net/ibm_emac/ibm_emac.h @@ -0,0 +1,263 @@ +/* + * ibm_emac.h + * + * + * Armin Kuster akuster@mvista.com + * June, 2002 + * + * Copyright 2002 MontaVista Softare Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef _IBM_EMAC_H_ +#define _IBM_EMAC_H_ +/* General defines needed for the driver */ + +/* Emac */ +typedef struct emac_regs { + u32 em0mr0; + u32 em0mr1; + u32 em0tmr0; + u32 em0tmr1; + u32 em0rmr; + u32 em0isr; + u32 em0iser; + u32 em0iahr; + u32 em0ialr; + u32 em0vtpid; + u32 em0vtci; + u32 em0ptr; + u32 em0iaht1; + u32 em0iaht2; + u32 em0iaht3; + u32 em0iaht4; + u32 em0gaht1; + u32 em0gaht2; + u32 em0gaht3; + u32 em0gaht4; + u32 em0lsah; + u32 em0lsal; + u32 em0ipgvr; + u32 em0stacr; + u32 em0trtr; + u32 em0rwmr; +} emac_t; + +/* MODE REG 0 */ +#define EMAC_M0_RXI 0x80000000 +#define EMAC_M0_TXI 0x40000000 +#define EMAC_M0_SRST 0x20000000 +#define EMAC_M0_TXE 0x10000000 +#define EMAC_M0_RXE 0x08000000 +#define EMAC_M0_WKE 0x04000000 + +/* MODE Reg 1 */ +#define EMAC_M1_FDE 0x80000000 +#define EMAC_M1_ILE 0x40000000 +#define EMAC_M1_VLE 0x20000000 +#define EMAC_M1_EIFC 0x10000000 +#define EMAC_M1_APP 0x08000000 +#define EMAC_M1_AEMI 0x02000000 +#define EMAC_M1_IST 0x01000000 +#define EMAC_M1_MF_1000GPCS 0x00c00000 /* Internal GPCS */ +#define EMAC_M1_MF_1000MBPS 0x00800000 /* External GPCS */ +#define EMAC_M1_MF_100MBPS 0x00400000 +#define EMAC_M1_RFS_16K 0x00280000 /* 000 for 512 byte */ +#define EMAC_M1_TR 0x00008000 +#ifdef CONFIG_IBM_EMAC4 +#define EMAC_M1_RFS_8K 0x00200000 +#define EMAC_M1_RFS_4K 0x00180000 +#define EMAC_M1_RFS_2K 0x00100000 +#define EMAC_M1_RFS_1K 0x00080000 +#define EMAC_M1_TX_FIFO_16K 0x00050000 /* 0's for 512 byte */ +#define EMAC_M1_TX_FIFO_8K 0x00040000 +#define EMAC_M1_TX_FIFO_4K 0x00030000 +#define EMAC_M1_TX_FIFO_2K 0x00020000 +#define EMAC_M1_TX_FIFO_1K 0x00010000 +#define EMAC_M1_TX_TR 0x00008000 +#define EMAC_M1_TX_MWSW 0x00001000 /* 0 wait for status */ +#define EMAC_M1_JUMBO_ENABLE 0x00000800 /* Upt to 9Kr status */ +#define EMAC_M1_OPB_CLK_66 0x00000008 /* 66Mhz */ +#define EMAC_M1_OPB_CLK_83 0x00000010 /* 83Mhz */ +#define EMAC_M1_OPB_CLK_100 0x00000018 /* 100Mhz */ +#define EMAC_M1_OPB_CLK_100P 0x00000020 /* 100Mhz+ */ +#else /* CONFIG_IBM_EMAC4 */ +#define EMAC_M1_RFS_4K 0x00300000 /* ~4k for 512 byte */ +#define EMAC_M1_RFS_2K 0x00200000 +#define EMAC_M1_RFS_1K 0x00100000 +#define EMAC_M1_TX_FIFO_2K 0x00080000 /* 0's for 512 byte */ +#define EMAC_M1_TX_FIFO_1K 0x00040000 +#define EMAC_M1_TR0_DEPEND 0x00010000 /* 0'x for single packet */ +#define EMAC_M1_TR1_DEPEND 0x00004000 +#define EMAC_M1_TR1_MULTI 0x00002000 +#define EMAC_M1_JUMBO_ENABLE 0x00001000 +#endif /* CONFIG_IBM_EMAC4 */ +#define EMAC_M1_BASE (EMAC_M1_TX_FIFO_2K | \ + EMAC_M1_APP | \ + EMAC_M1_TR) + +/* Transmit Mode Register 0 */ +#define EMAC_TMR0_GNP0 0x80000000 +#define EMAC_TMR0_GNP1 0x40000000 +#define EMAC_TMR0_GNPD 0x20000000 +#define EMAC_TMR0_FC 0x10000000 +#define EMAC_TMR0_TFAE_2_32 0x00000001 +#define EMAC_TMR0_TFAE_4_64 0x00000002 +#define EMAC_TMR0_TFAE_8_128 0x00000003 +#define EMAC_TMR0_TFAE_16_256 0x00000004 +#define EMAC_TMR0_TFAE_32_512 0x00000005 +#define EMAC_TMR0_TFAE_64_1024 0x00000006 +#define EMAC_TMR0_TFAE_128_2048 0x00000007 + +/* Receive Mode Register */ +#define EMAC_RMR_SP 0x80000000 +#define EMAC_RMR_SFCS 0x40000000 +#define EMAC_RMR_ARRP 0x20000000 +#define EMAC_RMR_ARP 0x10000000 +#define EMAC_RMR_AROP 0x08000000 +#define EMAC_RMR_ARPI 0x04000000 +#define EMAC_RMR_PPP 0x02000000 +#define EMAC_RMR_PME 0x01000000 +#define EMAC_RMR_PMME 0x00800000 +#define EMAC_RMR_IAE 0x00400000 +#define EMAC_RMR_MIAE 0x00200000 +#define EMAC_RMR_BAE 0x00100000 +#define EMAC_RMR_MAE 0x00080000 +#define EMAC_RMR_RFAF_2_32 0x00000001 +#define EMAC_RMR_RFAF_4_64 0x00000002 +#define EMAC_RMR_RFAF_8_128 0x00000003 +#define EMAC_RMR_RFAF_16_256 0x00000004 +#define EMAC_RMR_RFAF_32_512 0x00000005 +#define EMAC_RMR_RFAF_64_1024 0x00000006 +#define EMAC_RMR_RFAF_128_2048 0x00000007 +#define EMAC_RMR_BASE (EMAC_RMR_IAE | EMAC_RMR_BAE) + +/* Interrupt Status & enable Regs */ +#define EMAC_ISR_OVR 0x02000000 +#define EMAC_ISR_PP 0x01000000 +#define EMAC_ISR_BP 0x00800000 +#define EMAC_ISR_RP 0x00400000 +#define EMAC_ISR_SE 0x00200000 +#define EMAC_ISR_ALE 0x00100000 +#define EMAC_ISR_BFCS 0x00080000 +#define EMAC_ISR_PTLE 0x00040000 +#define EMAC_ISR_ORE 0x00020000 +#define EMAC_ISR_IRE 0x00010000 +#define EMAC_ISR_DBDM 0x00000200 +#define EMAC_ISR_DB0 0x00000100 +#define EMAC_ISR_SE0 0x00000080 +#define EMAC_ISR_TE0 0x00000040 +#define EMAC_ISR_DB1 0x00000020 +#define EMAC_ISR_SE1 0x00000010 +#define EMAC_ISR_TE1 0x00000008 +#define EMAC_ISR_MOS 0x00000002 +#define EMAC_ISR_MOF 0x00000001 + +/* STA CONTROL REG */ +#define EMAC_STACR_OC 0x00008000 +#define EMAC_STACR_PHYE 0x00004000 +#define EMAC_STACR_WRITE 0x00002000 +#define EMAC_STACR_READ 0x00001000 +#define EMAC_STACR_CLK_83MHZ 0x00000800 /* 0's for 50Mhz */ +#define EMAC_STACR_CLK_66MHZ 0x00000400 +#define EMAC_STACR_CLK_100MHZ 0x00000C00 + +/* Transmit Request Threshold Register */ +#define EMAC_TRTR_1600 0x18000000 /* 0's for 64 Bytes */ +#define EMAC_TRTR_1024 0x0f000000 +#define EMAC_TRTR_512 0x07000000 +#define EMAC_TRTR_256 0x03000000 +#define EMAC_TRTR_192 0x10000000 +#define EMAC_TRTR_128 0x01000000 + +#define EMAC_TX_CTRL_GFCS 0x0200 +#define EMAC_TX_CTRL_GP 0x0100 +#define EMAC_TX_CTRL_ISA 0x0080 +#define EMAC_TX_CTRL_RSA 0x0040 +#define EMAC_TX_CTRL_IVT 0x0020 +#define EMAC_TX_CTRL_RVT 0x0010 +#define EMAC_TX_CTRL_TAH_CSUM 0x000e /* TAH only */ +#define EMAC_TX_CTRL_TAH_SEG4 0x000a /* TAH only */ +#define EMAC_TX_CTRL_TAH_SEG3 0x0008 /* TAH only */ +#define EMAC_TX_CTRL_TAH_SEG2 0x0006 /* TAH only */ +#define EMAC_TX_CTRL_TAH_SEG1 0x0004 /* TAH only */ +#define EMAC_TX_CTRL_TAH_SEG0 0x0002 /* TAH only */ +#define EMAC_TX_CTRL_TAH_DIS 0x0000 /* TAH only */ + +#define EMAC_TX_CTRL_DFLT ( \ + MAL_TX_CTRL_INTR | EMAC_TX_CTRL_GFCS | EMAC_TX_CTRL_GP ) + +/* madmal transmit status / Control bits */ +#define EMAC_TX_ST_BFCS 0x0200 +#define EMAC_TX_ST_BPP 0x0100 +#define EMAC_TX_ST_LCS 0x0080 +#define EMAC_TX_ST_ED 0x0040 +#define EMAC_TX_ST_EC 0x0020 +#define EMAC_TX_ST_LC 0x0010 +#define EMAC_TX_ST_MC 0x0008 +#define EMAC_TX_ST_SC 0x0004 +#define EMAC_TX_ST_UR 0x0002 +#define EMAC_TX_ST_SQE 0x0001 + +/* madmal receive status / Control bits */ +#define EMAC_RX_ST_OE 0x0200 +#define EMAC_RX_ST_PP 0x0100 +#define EMAC_RX_ST_BP 0x0080 +#define EMAC_RX_ST_RP 0x0040 +#define EMAC_RX_ST_SE 0x0020 +#define EMAC_RX_ST_AE 0x0010 +#define EMAC_RX_ST_BFCS 0x0008 +#define EMAC_RX_ST_PTL 0x0004 +#define EMAC_RX_ST_ORE 0x0002 +#define EMAC_RX_ST_IRE 0x0001 +#define EMAC_BAD_RX_PACKET 0x02ff +#define EMAC_CSUM_VER_ERROR 0x0003 + +/* identify a bad rx packet dependent on emac features */ +#ifdef CONFIG_IBM_EMAC4 +#define EMAC_IS_BAD_RX_PACKET(desc) \ + (((desc & (EMAC_BAD_RX_PACKET & ~EMAC_CSUM_VER_ERROR)) || \ + ((desc & EMAC_CSUM_VER_ERROR) == EMAC_RX_ST_ORE) || \ + ((desc & EMAC_CSUM_VER_ERROR) == EMAC_RX_ST_IRE))) +#else +#define EMAC_IS_BAD_RX_PACKET(desc) \ + (desc & EMAC_BAD_RX_PACKET) +#endif + +/* Revision specific EMAC register defaults */ +#ifdef CONFIG_IBM_EMAC4 +#define EMAC_M1_DEFAULT (EMAC_M1_BASE | \ + EMAC_M1_OPB_CLK_83 | \ + EMAC_M1_TX_MWSW) +#define EMAC_RMR_DEFAULT (EMAC_RMR_BASE | \ + EMAC_RMR_RFAF_128_2048) +#define EMAC_TMR0_XMIT (EMAC_TMR0_GNP0 | \ + EMAC_TMR0_TFAE_128_2048) +#define EMAC_TRTR_DEFAULT EMAC_TRTR_1024 +#else /* !CONFIG_IBM_EMAC4 */ +#define EMAC_M1_DEFAULT EMAC_M1_BASE +#define EMAC_RMR_DEFAULT EMAC_RMR_BASE +#define EMAC_TMR0_XMIT EMAC_TMR0_GNP0 +#define EMAC_TRTR_DEFAULT EMAC_TRTR_1600 +#endif /* CONFIG_IBM_EMAC4 */ + +/* SoC implementation specific EMAC register defaults */ +#if defined(CONFIG_440GP) +#define EMAC_RWMR_DEFAULT 0x80009000 +#define EMAC_TMR0_DEFAULT 0x00000000 +#define EMAC_TMR1_DEFAULT 0xf8640000 +#elif defined(CONFIG_440GX) +#define EMAC_RWMR_DEFAULT 0x1000a200 +#define EMAC_TMR0_DEFAULT EMAC_TMR0_TFAE_128_2048 +#define EMAC_TMR1_DEFAULT 0x88810000 +#else +#define EMAC_RWMR_DEFAULT 0x0f002000 +#define EMAC_TMR0_DEFAULT 0x00000000 +#define EMAC_TMR1_DEFAULT 0x380f0000 +#endif /* CONFIG_440GP */ + +#endif diff --git a/drivers/net/ibm_emac/ibm_emac_core.h b/drivers/net/ibm_emac/ibm_emac_core.h new file mode 100644 index 000000000..691ce4e5c --- /dev/null +++ b/drivers/net/ibm_emac/ibm_emac_core.h @@ -0,0 +1,146 @@ +/* + * ibm_emac_core.h + * + * Ethernet driver for the built in ethernet on the IBM 405 PowerPC + * processor. + * + * Armin Kuster akuster@mvista.com + * Sept, 2001 + * + * Orignial driver + * Johnnie Peters + * jpeters@mvista.com + * + * Copyright 2000 MontaVista Softare Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef _IBM_EMAC_CORE_H_ +#define _IBM_EMAC_CORE_H_ + +#include +#include +#include /* For phys_addr_t */ + +#include "ibm_emac.h" +#include "ibm_emac_phy.h" +#include "ibm_emac_rgmii.h" +#include "ibm_emac_zmii.h" +#include "ibm_emac_mal.h" +#include "ibm_emac_tah.h" + +#ifndef CONFIG_IBM_EMAC_TXB +#define NUM_TX_BUFF 64 +#define NUM_RX_BUFF 64 +#else +#define NUM_TX_BUFF CONFIG_IBM_EMAC_TXB +#define NUM_RX_BUFF CONFIG_IBM_EMAC_RXB +#endif + +/* This does 16 byte alignment, exactly what we need. + * The packet length includes FCS, but we don't want to + * include that when passing upstream as it messes up + * bridging applications. + */ +#ifndef CONFIG_IBM_EMAC_SKBRES +#define SKB_RES 2 +#else +#define SKB_RES CONFIG_IBM_EMAC_SKBRES +#endif + +/* Note about alignement. alloc_skb() returns a cache line + * aligned buffer. However, dev_alloc_skb() will add 16 more + * bytes and "reserve" them, so our buffer will actually end + * on a half cache line. What we do is to use directly + * alloc_skb, allocate 16 more bytes to match the total amount + * allocated by dev_alloc_skb(), but we don't reserve. + */ +#define MAX_NUM_BUF_DESC 255 +#define DESC_BUF_SIZE 4080 /* max 4096-16 */ +#define DESC_BUF_SIZE_REG (DESC_BUF_SIZE / 16) + +/* Transmitter timeout. */ +#define TX_TIMEOUT (2*HZ) + +/* MDIO latency delay */ +#define MDIO_DELAY 50 + +/* Power managment shift registers */ +#define IBM_CPM_EMMII 0 /* Shift value for MII */ +#define IBM_CPM_EMRX 1 /* Shift value for recv */ +#define IBM_CPM_EMTX 2 /* Shift value for MAC */ +#define IBM_CPM_EMAC(x) (((x)>>IBM_CPM_EMMII) | ((x)>>IBM_CPM_EMRX) | ((x)>>IBM_CPM_EMTX)) + +#define ENET_HEADER_SIZE 14 +#define ENET_FCS_SIZE 4 +#define ENET_DEF_MTU_SIZE 1500 +#define ENET_DEF_BUF_SIZE (ENET_DEF_MTU_SIZE + ENET_HEADER_SIZE + ENET_FCS_SIZE) +#define EMAC_MIN_FRAME 64 +#define EMAC_MAX_FRAME 9018 +#define EMAC_MIN_MTU (EMAC_MIN_FRAME - ENET_HEADER_SIZE - ENET_FCS_SIZE) +#define EMAC_MAX_MTU (EMAC_MAX_FRAME - ENET_HEADER_SIZE - ENET_FCS_SIZE) + +#ifdef CONFIG_IBM_EMAC_ERRMSG +void emac_serr_dump_0(struct net_device *dev); +void emac_serr_dump_1(struct net_device *dev); +void emac_err_dump(struct net_device *dev, int em0isr); +void emac_phy_dump(struct net_device *); +void emac_desc_dump(struct net_device *); +void emac_mac_dump(struct net_device *); +void emac_mal_dump(struct net_device *); +#else +#define emac_serr_dump_0(dev) do { } while (0) +#define emac_serr_dump_1(dev) do { } while (0) +#define emac_err_dump(dev,x) do { } while (0) +#define emac_phy_dump(dev) do { } while (0) +#define emac_desc_dump(dev) do { } while (0) +#define emac_mac_dump(dev) do { } while (0) +#define emac_mal_dump(dev) do { } while (0) +#endif + +struct ocp_enet_private { + struct sk_buff *tx_skb[NUM_TX_BUFF]; + struct sk_buff *rx_skb[NUM_RX_BUFF]; + struct mal_descriptor *tx_desc; + struct mal_descriptor *rx_desc; + struct mal_descriptor *rx_dirty; + struct net_device_stats stats; + int tx_cnt; + int rx_slot; + int dirty_rx; + int tx_slot; + int ack_slot; + int rx_buffer_size; + + struct mii_phy phy_mii; + int mii_phy_addr; + int want_autoneg; + int timer_ticks; + struct timer_list link_timer; + struct net_device *mdio_dev; + + struct ocp_device *rgmii_dev; + int rgmii_input; + + struct ocp_device *zmii_dev; + int zmii_input; + + struct ibm_ocp_mal *mal; + int mal_tx_chan, mal_rx_chan; + struct mal_commac commac; + + struct ocp_device *tah_dev; + + int opened; + int going_away; + int wol_irq; + emac_t *emacp; + struct ocp_device *ocpdev; + struct net_device *ndev; + spinlock_t lock; +}; +#endif /* _IBM_EMAC_CORE_H_ */ diff --git a/drivers/net/ibm_emac/ibm_emac_debug.c b/drivers/net/ibm_emac/ibm_emac_debug.c new file mode 100644 index 000000000..c8512046c --- /dev/null +++ b/drivers/net/ibm_emac/ibm_emac_debug.c @@ -0,0 +1,224 @@ +/* + * ibm_ocp_debug.c + * + * This has all the debug routines that where in *_enet.c + * + * Armin Kuster akuster@mvista.com + * April , 2002 + * + * Copyright 2002 MontaVista Softare Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include "ibm_ocp_mal.h" +#include "ibm_ocp_zmii.h" +#include "ibm_ocp_enet.h" + +extern int emac_phy_read(struct net_device *dev, int mii_id, int reg); + +void emac_phy_dump(struct net_device *dev) +{ + struct ocp_enet_private *fep = dev->priv; + unsigned long i; + uint data; + + printk(KERN_DEBUG " Prepare for Phy dump....\n"); + for (i = 0; i < 0x1A; i++) { + data = emac_phy_read(dev, fep->mii_phy_addr, i); + printk(KERN_DEBUG "Phy reg 0x%lx ==> %4x\n", i, data); + if (i == 0x07) + i = 0x0f; + } +} + +void emac_desc_dump(struct net_device *dev) +{ + struct ocp_enet_private *fep = dev->priv; + int curr_slot; + + printk(KERN_DEBUG + "dumping the receive descriptors: current slot is %d\n", + fep->rx_slot); + for (curr_slot = 0; curr_slot < NUM_RX_BUFF; curr_slot++) { + printk(KERN_DEBUG + "Desc %02d: status 0x%04x, length %3d, addr 0x%x\n", + curr_slot, fep->rx_desc[curr_slot].ctrl, + fep->rx_desc[curr_slot].data_len, + (unsigned int)fep->rx_desc[curr_slot].data_ptr); + } +} + +void emac_mac_dump(struct net_device *dev) +{ + struct ocp_enet_private *fep = dev->priv; + volatile emac_t *emacp = fep->emacp; + + printk(KERN_DEBUG "EMAC DEBUG ********** \n"); + printk(KERN_DEBUG "EMAC_M0 ==> 0x%x\n", in_be32(&emacp->em0mr0)); + printk(KERN_DEBUG "EMAC_M1 ==> 0x%x\n", in_be32(&emacp->em0mr1)); + printk(KERN_DEBUG "EMAC_TXM0==> 0x%x\n", in_be32(&emacp->em0tmr0)); + printk(KERN_DEBUG "EMAC_TXM1==> 0x%x\n", in_be32(&emacp->em0tmr1)); + printk(KERN_DEBUG "EMAC_RXM ==> 0x%x\n", in_be32(&emacp->em0rmr)); + printk(KERN_DEBUG "EMAC_ISR ==> 0x%x\n", in_be32(&emacp->em0isr)); + printk(KERN_DEBUG "EMAC_IER ==> 0x%x\n", in_be32(&emacp->em0iser)); + printk(KERN_DEBUG "EMAC_IAH ==> 0x%x\n", in_be32(&emacp->em0iahr)); + printk(KERN_DEBUG "EMAC_IAL ==> 0x%x\n", in_be32(&emacp->em0ialr)); + printk(KERN_DEBUG "EMAC_VLAN_TPID_REG ==> 0x%x\n", + in_be32(&emacp->em0vtpid)); +} + +void emac_mal_dump(struct net_device *dev) +{ + struct ibm_ocp_mal *mal = ((struct ocp_enet_private *)dev->priv)->mal; + + printk(KERN_DEBUG " MAL DEBUG ********** \n"); + printk(KERN_DEBUG " MCR ==> 0x%x\n", + (unsigned int)get_mal_dcrn(mal, DCRN_MALCR)); + printk(KERN_DEBUG " ESR ==> 0x%x\n", + (unsigned int)get_mal_dcrn(mal, DCRN_MALESR)); + printk(KERN_DEBUG " IER ==> 0x%x\n", + (unsigned int)get_mal_dcrn(mal, DCRN_MALIER)); +#ifdef CONFIG_40x + printk(KERN_DEBUG " DBR ==> 0x%x\n", + (unsigned int)get_mal_dcrn(mal, DCRN_MALDBR)); +#endif /* CONFIG_40x */ + printk(KERN_DEBUG " TXCASR ==> 0x%x\n", + (unsigned int)get_mal_dcrn(mal, DCRN_MALTXCASR)); + printk(KERN_DEBUG " TXCARR ==> 0x%x\n", + (unsigned int)get_mal_dcrn(mal, DCRN_MALTXCARR)); + printk(KERN_DEBUG " TXEOBISR ==> 0x%x\n", + (unsigned int)get_mal_dcrn(mal, DCRN_MALTXEOBISR)); + printk(KERN_DEBUG " TXDEIR ==> 0x%x\n", + (unsigned int)get_mal_dcrn(mal, DCRN_MALTXDEIR)); + printk(KERN_DEBUG " RXCASR ==> 0x%x\n", + (unsigned int)get_mal_dcrn(mal, DCRN_MALRXCASR)); + printk(KERN_DEBUG " RXCARR ==> 0x%x\n", + (unsigned int)get_mal_dcrn(mal, DCRN_MALRXCARR)); + printk(KERN_DEBUG " RXEOBISR ==> 0x%x\n", + (unsigned int)get_mal_dcrn(mal, DCRN_MALRXEOBISR)); + printk(KERN_DEBUG " RXDEIR ==> 0x%x\n", + (unsigned int)get_mal_dcrn(mal, DCRN_MALRXDEIR)); + printk(KERN_DEBUG " TXCTP0R ==> 0x%x\n", + (unsigned int)get_mal_dcrn(mal, DCRN_MALTXCTP0R)); + printk(KERN_DEBUG " TXCTP1R ==> 0x%x\n", + (unsigned int)get_mal_dcrn(mal, DCRN_MALTXCTP1R)); + printk(KERN_DEBUG " TXCTP2R ==> 0x%x\n", + (unsigned int)get_mal_dcrn(mal, DCRN_MALTXCTP2R)); + printk(KERN_DEBUG " TXCTP3R ==> 0x%x\n", + (unsigned int)get_mal_dcrn(mal, DCRN_MALTXCTP3R)); + printk(KERN_DEBUG " RXCTP0R ==> 0x%x\n", + (unsigned int)get_mal_dcrn(mal, DCRN_MALRXCTP0R)); + printk(KERN_DEBUG " RXCTP1R ==> 0x%x\n", + (unsigned int)get_mal_dcrn(mal, DCRN_MALRXCTP1R)); + printk(KERN_DEBUG " RCBS0 ==> 0x%x\n", + (unsigned int)get_mal_dcrn(mal, DCRN_MALRCBS0)); + printk(KERN_DEBUG " RCBS1 ==> 0x%x\n", + (unsigned int)get_mal_dcrn(mal, DCRN_MALRCBS1)); +} + +void emac_serr_dump_0(struct net_device *dev) +{ + struct ibm_ocp_mal *mal = ((struct ocp_enet_private *)dev->priv)->mal; + unsigned long int mal_error, plb_error, plb_addr; + + mal_error = get_mal_dcrn(mal, DCRN_MALESR); + printk(KERN_DEBUG "ppc405_eth_serr: %s channel %ld \n", + (mal_error & 0x40000000) ? "Receive" : + "Transmit", (mal_error & 0x3e000000) >> 25); + printk(KERN_DEBUG " ----- latched error -----\n"); + if (mal_error & MALESR_DE) + printk(KERN_DEBUG " DE: descriptor error\n"); + if (mal_error & MALESR_OEN) + printk(KERN_DEBUG " ONE: OPB non-fullword error\n"); + if (mal_error & MALESR_OTE) + printk(KERN_DEBUG " OTE: OPB timeout error\n"); + if (mal_error & MALESR_OSE) + printk(KERN_DEBUG " OSE: OPB slave error\n"); + + if (mal_error & MALESR_PEIN) { + plb_error = mfdcr(DCRN_PLB0_BESR); + printk(KERN_DEBUG + " PEIN: PLB error, PLB0_BESR is 0x%x\n", + (unsigned int)plb_error); + plb_addr = mfdcr(DCRN_PLB0_BEAR); + printk(KERN_DEBUG + " PEIN: PLB error, PLB0_BEAR is 0x%x\n", + (unsigned int)plb_addr); + } +} + +void emac_serr_dump_1(struct net_device *dev) +{ + struct ibm_ocp_mal *mal = ((struct ocp_enet_private *)dev->priv)->mal; + int mal_error = get_mal_dcrn(mal, DCRN_MALESR); + + printk(KERN_DEBUG " ----- cumulative errors -----\n"); + if (mal_error & MALESR_DEI) + printk(KERN_DEBUG " DEI: descriptor error interrupt\n"); + if (mal_error & MALESR_ONEI) + printk(KERN_DEBUG " OPB non-fullword error interrupt\n"); + if (mal_error & MALESR_OTEI) + printk(KERN_DEBUG " OTEI: timeout error interrupt\n"); + if (mal_error & MALESR_OSEI) + printk(KERN_DEBUG " OSEI: slave error interrupt\n"); + if (mal_error & MALESR_PBEI) + printk(KERN_DEBUG " PBEI: PLB bus error interrupt\n"); +} + +void emac_err_dump(struct net_device *dev, int em0isr) +{ + printk(KERN_DEBUG "%s: on-chip ethernet error:\n", dev->name); + + if (em0isr & EMAC_ISR_OVR) + printk(KERN_DEBUG " OVR: overrun\n"); + if (em0isr & EMAC_ISR_PP) + printk(KERN_DEBUG " PP: control pause packet\n"); + if (em0isr & EMAC_ISR_BP) + printk(KERN_DEBUG " BP: packet error\n"); + if (em0isr & EMAC_ISR_RP) + printk(KERN_DEBUG " RP: runt packet\n"); + if (em0isr & EMAC_ISR_SE) + printk(KERN_DEBUG " SE: short event\n"); + if (em0isr & EMAC_ISR_ALE) + printk(KERN_DEBUG " ALE: odd number of nibbles in packet\n"); + if (em0isr & EMAC_ISR_BFCS) + printk(KERN_DEBUG " BFCS: bad FCS\n"); + if (em0isr & EMAC_ISR_PTLE) + printk(KERN_DEBUG " PTLE: oversized packet\n"); + if (em0isr & EMAC_ISR_ORE) + printk(KERN_DEBUG + " ORE: packet length field > max allowed LLC\n"); + if (em0isr & EMAC_ISR_IRE) + printk(KERN_DEBUG " IRE: In Range error\n"); + if (em0isr & EMAC_ISR_DBDM) + printk(KERN_DEBUG " DBDM: xmit error or SQE\n"); + if (em0isr & EMAC_ISR_DB0) + printk(KERN_DEBUG " DB0: xmit error or SQE on TX channel 0\n"); + if (em0isr & EMAC_ISR_SE0) + printk(KERN_DEBUG + " SE0: Signal Quality Error test failure from TX channel 0\n"); + if (em0isr & EMAC_ISR_TE0) + printk(KERN_DEBUG " TE0: xmit channel 0 aborted\n"); + if (em0isr & EMAC_ISR_DB1) + printk(KERN_DEBUG " DB1: xmit error or SQE on TX channel \n"); + if (em0isr & EMAC_ISR_SE1) + printk(KERN_DEBUG + " SE1: Signal Quality Error test failure from TX channel 1\n"); + if (em0isr & EMAC_ISR_TE1) + printk(KERN_DEBUG " TE1: xmit channel 1 aborted\n"); + if (em0isr & EMAC_ISR_MOS) + printk(KERN_DEBUG " MOS\n"); + if (em0isr & EMAC_ISR_MOF) + printk(KERN_DEBUG " MOF\n"); + + emac_mac_dump(dev); + emac_mal_dump(dev); +} diff --git a/drivers/net/ibm_emac/ibm_emac_mal.c b/drivers/net/ibm_emac/ibm_emac_mal.c new file mode 100644 index 000000000..02d847cfa --- /dev/null +++ b/drivers/net/ibm_emac/ibm_emac_mal.c @@ -0,0 +1,467 @@ +/* + * ibm_ocp_mal.c + * + * Armin Kuster akuster@mvista.com + * Juen, 2002 + * + * Copyright 2002 MontaVista Softare Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "ibm_emac_mal.h" + +// Locking: Should we share a lock with the client ? The client could provide +// a lock pointer (optionally) in the commac structure... I don't think this is +// really necessary though + +/* This lock protects the commac list. On today UP implementations, it's + * really only used as IRQ protection in mal_{register,unregister}_commac() + */ +static rwlock_t mal_list_lock = RW_LOCK_UNLOCKED; + +int mal_register_commac(struct ibm_ocp_mal *mal, struct mal_commac *commac) +{ + unsigned long flags; + + write_lock_irqsave(&mal_list_lock, flags); + + /* Don't let multiple commacs claim the same channel */ + if ((mal->tx_chan_mask & commac->tx_chan_mask) || + (mal->rx_chan_mask & commac->rx_chan_mask)) { + write_unlock_irqrestore(&mal_list_lock, flags); + return -EBUSY; + } + + mal->tx_chan_mask |= commac->tx_chan_mask; + mal->rx_chan_mask |= commac->rx_chan_mask; + + list_add(&commac->list, &mal->commac); + + write_unlock_irqrestore(&mal_list_lock, flags); + + MOD_INC_USE_COUNT; + + return 0; +} + +int mal_unregister_commac(struct ibm_ocp_mal *mal, struct mal_commac *commac) +{ + unsigned long flags; + + write_lock_irqsave(&mal_list_lock, flags); + + mal->tx_chan_mask &= ~commac->tx_chan_mask; + mal->rx_chan_mask &= ~commac->rx_chan_mask; + + list_del_init(&commac->list); + + write_unlock_irqrestore(&mal_list_lock, flags); + + MOD_DEC_USE_COUNT; + + return 0; +} + +int mal_set_rcbs(struct ibm_ocp_mal *mal, int channel, unsigned long size) +{ + switch (channel) { + case 0: + set_mal_dcrn(mal, DCRN_MALRCBS0, size); + break; +#ifdef DCRN_MALRCBS1 + case 1: + set_mal_dcrn(mal, DCRN_MALRCBS1, size); + break; +#endif +#ifdef DCRN_MALRCBS2 + case 2: + set_mal_dcrn(mal, DCRN_MALRCBS2, size); + break; +#endif +#ifdef DCRN_MALRCBS3 + case 3: + set_mal_dcrn(mal, DCRN_MALRCBS3, size); + break; +#endif + default: + return -EINVAL; + } + + return 0; +} + +static irqreturn_t mal_serr(int irq, void *dev_instance, struct pt_regs *regs) +{ + struct ibm_ocp_mal *mal = dev_instance; + unsigned long mal_error; + + /* + * This SERR applies to one of the devices on the MAL, here we charge + * it against the first EMAC registered for the MAL. + */ + + mal_error = get_mal_dcrn(mal, DCRN_MALESR); + + printk(KERN_ERR "%s: System Error (MALESR=%lx)\n", + "MAL" /* FIXME: get the name right */ , mal_error); + + /* FIXME: decipher error */ + /* DIXME: distribute to commacs, if possible */ + + /* Clear the error status register */ + set_mal_dcrn(mal, DCRN_MALESR, mal_error); + + return IRQ_HANDLED; +} + +static irqreturn_t mal_txeob(int irq, void *dev_instance, struct pt_regs *regs) +{ + struct ibm_ocp_mal *mal = dev_instance; + struct list_head *l; + unsigned long isr; + + isr = get_mal_dcrn(mal, DCRN_MALTXEOBISR); + set_mal_dcrn(mal, DCRN_MALTXEOBISR, isr); + + read_lock(&mal_list_lock); + list_for_each(l, &mal->commac) { + struct mal_commac *mc = list_entry(l, struct mal_commac, list); + + if (isr & mc->tx_chan_mask) { + mc->ops->txeob(mc->dev, isr & mc->tx_chan_mask); + } + } + read_unlock(&mal_list_lock); + + return IRQ_HANDLED; +} + +static irqreturn_t mal_rxeob(int irq, void *dev_instance, struct pt_regs *regs) +{ + struct ibm_ocp_mal *mal = dev_instance; + struct list_head *l; + unsigned long isr; + + isr = get_mal_dcrn(mal, DCRN_MALRXEOBISR); + set_mal_dcrn(mal, DCRN_MALRXEOBISR, isr); + + read_lock(&mal_list_lock); + list_for_each(l, &mal->commac) { + struct mal_commac *mc = list_entry(l, struct mal_commac, list); + + if (isr & mc->rx_chan_mask) { + mc->ops->rxeob(mc->dev, isr & mc->rx_chan_mask); + } + } + read_unlock(&mal_list_lock); + + return IRQ_HANDLED; +} + +static irqreturn_t mal_txde(int irq, void *dev_instance, struct pt_regs *regs) +{ + struct ibm_ocp_mal *mal = dev_instance; + struct list_head *l; + unsigned long deir; + + deir = get_mal_dcrn(mal, DCRN_MALTXDEIR); + + /* FIXME: print which MAL correctly */ + printk(KERN_WARNING "%s: Tx descriptor error (MALTXDEIR=%lx)\n", + "MAL", deir); + + read_lock(&mal_list_lock); + list_for_each(l, &mal->commac) { + struct mal_commac *mc = list_entry(l, struct mal_commac, list); + + if (deir & mc->tx_chan_mask) { + mc->ops->txde(mc->dev, deir & mc->tx_chan_mask); + } + } + read_unlock(&mal_list_lock); + + return IRQ_HANDLED; +} + +/* + * This interrupt should be very rare at best. This occurs when + * the hardware has a problem with the receive descriptors. The manual + * states that it occurs when the hardware cannot the receive descriptor + * empty bit is not set. The recovery mechanism will be to + * traverse through the descriptors, handle any that are marked to be + * handled and reinitialize each along the way. At that point the driver + * will be restarted. + */ +static irqreturn_t mal_rxde(int irq, void *dev_instance, struct pt_regs *regs) +{ + struct ibm_ocp_mal *mal = dev_instance; + struct list_head *l; + unsigned long deir; + + deir = get_mal_dcrn(mal, DCRN_MALRXDEIR); + + /* + * This really is needed. This case encountered in stress testing. + */ + if (deir == 0) + return IRQ_HANDLED; + + /* FIXME: print which MAL correctly */ + printk(KERN_WARNING "%s: Rx descriptor error (MALRXDEIR=%lx)\n", + "MAL", deir); + + read_lock(&mal_list_lock); + list_for_each(l, &mal->commac) { + struct mal_commac *mc = list_entry(l, struct mal_commac, list); + + if (deir & mc->rx_chan_mask) { + mc->ops->rxde(mc->dev, deir & mc->rx_chan_mask); + } + } + read_unlock(&mal_list_lock); + + return IRQ_HANDLED; +} + +static int __init mal_probe(struct ocp_device *ocpdev) +{ + struct ibm_ocp_mal *mal = NULL; + struct ocp_func_mal_data *maldata; + int err = 0; + + maldata = (struct ocp_func_mal_data *)ocpdev->def->additions; + if (maldata == NULL) { + printk(KERN_ERR "mal%d: Missing additional datas !\n", + ocpdev->def->index); + return -ENODEV; + } + + mal = kmalloc(sizeof(struct ibm_ocp_mal), GFP_KERNEL); + if (mal == NULL) { + printk(KERN_ERR + "mal%d: Out of memory allocating MAL structure !\n", + ocpdev->def->index); + return -ENOMEM; + } + memset(mal, 0, sizeof(*mal)); + + switch (ocpdev->def->index) { + case 0: + mal->dcrbase = DCRN_MAL_BASE; + break; +#ifdef DCRN_MAL1_BASE + case 1: + mal->dcrbase = DCRN_MAL1_BASE; + break; +#endif + default: + BUG(); + } + + /**************************/ + + INIT_LIST_HEAD(&mal->commac); + + set_mal_dcrn(mal, DCRN_MALRXCARR, 0xFFFFFFFF); + set_mal_dcrn(mal, DCRN_MALTXCARR, 0xFFFFFFFF); + + set_mal_dcrn(mal, DCRN_MALCR, MALCR_MMSR); /* 384 */ + /* FIXME: Add delay */ + + /* Set the MAL configuration register */ + set_mal_dcrn(mal, DCRN_MALCR, + MALCR_PLBB | MALCR_OPBBL | MALCR_LEA | + MALCR_PLBLT_DEFAULT); + + /* It would be nice to allocate buffers separately for each + * channel, but we can't because the channels share the upper + * 13 bits of address lines. Each channels buffer must also + * be 4k aligned, so we allocate 4k for each channel. This is + * inefficient FIXME: do better, if possible */ + mal->tx_virt_addr = dma_alloc_coherent(&ocpdev->dev, + MAL_DT_ALIGN * + maldata->num_tx_chans, + &mal->tx_phys_addr, GFP_KERNEL); + if (mal->tx_virt_addr == NULL) { + printk(KERN_ERR + "mal%d: Out of memory allocating MAL descriptors !\n", + ocpdev->def->index); + err = -ENOMEM; + goto fail; + } + + /* God, oh, god, I hate DCRs */ + set_mal_dcrn(mal, DCRN_MALTXCTP0R, mal->tx_phys_addr); +#ifdef DCRN_MALTXCTP1R + if (maldata->num_tx_chans > 1) + set_mal_dcrn(mal, DCRN_MALTXCTP1R, + mal->tx_phys_addr + MAL_DT_ALIGN); +#endif /* DCRN_MALTXCTP1R */ +#ifdef DCRN_MALTXCTP2R + if (maldata->num_tx_chans > 2) + set_mal_dcrn(mal, DCRN_MALTXCTP2R, + mal->tx_phys_addr + 2 * MAL_DT_ALIGN); +#endif /* DCRN_MALTXCTP2R */ +#ifdef DCRN_MALTXCTP3R + if (maldata->num_tx_chans > 3) + set_mal_dcrn(mal, DCRN_MALTXCTP3R, + mal->tx_phys_addr + 3 * MAL_DT_ALIGN); +#endif /* DCRN_MALTXCTP3R */ +#ifdef DCRN_MALTXCTP4R + if (maldata->num_tx_chans > 4) + set_mal_dcrn(mal, DCRN_MALTXCTP4R, + mal->tx_phys_addr + 4 * MAL_DT_ALIGN); +#endif /* DCRN_MALTXCTP4R */ +#ifdef DCRN_MALTXCTP5R + if (maldata->num_tx_chans > 5) + set_mal_dcrn(mal, DCRN_MALTXCTP5R, + mal->tx_phys_addr + 5 * MAL_DT_ALIGN); +#endif /* DCRN_MALTXCTP5R */ +#ifdef DCRN_MALTXCTP6R + if (maldata->num_tx_chans > 6) + set_mal_dcrn(mal, DCRN_MALTXCTP6R, + mal->tx_phys_addr + 6 * MAL_DT_ALIGN); +#endif /* DCRN_MALTXCTP6R */ +#ifdef DCRN_MALTXCTP7R + if (maldata->num_tx_chans > 7) + set_mal_dcrn(mal, DCRN_MALTXCTP7R, + mal->tx_phys_addr + 7 * MAL_DT_ALIGN); +#endif /* DCRN_MALTXCTP7R */ + + mal->rx_virt_addr = dma_alloc_coherent(&ocpdev->dev, + MAL_DT_ALIGN * + maldata->num_rx_chans, + &mal->rx_phys_addr, GFP_KERNEL); + + set_mal_dcrn(mal, DCRN_MALRXCTP0R, mal->rx_phys_addr); +#ifdef DCRN_MALRXCTP1R + if (maldata->num_rx_chans > 1) + set_mal_dcrn(mal, DCRN_MALRXCTP1R, + mal->rx_phys_addr + MAL_DT_ALIGN); +#endif /* DCRN_MALRXCTP1R */ +#ifdef DCRN_MALRXCTP2R + if (maldata->num_rx_chans > 2) + set_mal_dcrn(mal, DCRN_MALRXCTP2R, + mal->rx_phys_addr + 2 * MAL_DT_ALIGN); +#endif /* DCRN_MALRXCTP2R */ +#ifdef DCRN_MALRXCTP3R + if (maldata->num_rx_chans > 3) + set_mal_dcrn(mal, DCRN_MALRXCTP3R, + mal->rx_phys_addr + 3 * MAL_DT_ALIGN); +#endif /* DCRN_MALRXCTP3R */ + + err = request_irq(maldata->serr_irq, mal_serr, 0, "MAL SERR", mal); + if (err) + goto fail; + err = request_irq(maldata->txde_irq, mal_txde, 0, "MAL TX DE ", mal); + if (err) + goto fail; + err = request_irq(maldata->txeob_irq, mal_txeob, 0, "MAL TX EOB", mal); + if (err) + goto fail; + err = request_irq(maldata->rxde_irq, mal_rxde, 0, "MAL RX DE", mal); + if (err) + goto fail; + err = request_irq(maldata->rxeob_irq, mal_rxeob, 0, "MAL RX EOB", mal); + if (err) + goto fail; + + set_mal_dcrn(mal, DCRN_MALIER, + MALIER_DE | MALIER_NE | MALIER_TE | + MALIER_OPBE | MALIER_PLBE); + + /* Advertise me to the rest of the world */ + ocp_set_drvdata(ocpdev, mal); + + printk(KERN_INFO "mal%d: Initialized, %d tx channels, %d rx channels\n", + ocpdev->def->index, maldata->num_tx_chans, + maldata->num_rx_chans); + + return 0; + + fail: + /* FIXME: dispose requested IRQs ! */ + if (err && mal) + kfree(mal); + return err; +} + +static void __exit mal_remove(struct ocp_device *ocpdev) +{ + struct ibm_ocp_mal *mal = ocp_get_drvdata(ocpdev); + struct ocp_func_mal_data *maldata = ocpdev->def->additions; + + BUG_ON(!maldata); + + ocp_set_drvdata(ocpdev, NULL); + + /* FIXME: shut down the MAL, deal with dependency with emac */ + free_irq(maldata->serr_irq, mal); + free_irq(maldata->txde_irq, mal); + free_irq(maldata->txeob_irq, mal); + free_irq(maldata->rxde_irq, mal); + free_irq(maldata->rxeob_irq, mal); + + if (mal->tx_virt_addr) + dma_free_coherent(&ocpdev->dev, + MAL_DT_ALIGN * maldata->num_tx_chans, + mal->tx_virt_addr, mal->tx_phys_addr); + + if (mal->rx_virt_addr) + dma_free_coherent(&ocpdev->dev, + MAL_DT_ALIGN * maldata->num_rx_chans, + mal->rx_virt_addr, mal->rx_phys_addr); + + kfree(mal); +} + +/* Structure for a device driver */ +static struct ocp_device_id mal_ids[] = { + {.vendor = OCP_ANY_ID,.function = OCP_FUNC_MAL}, + {.vendor = OCP_VENDOR_INVALID} +}; + +static struct ocp_driver mal_driver = { + .name = "mal", + .id_table = mal_ids, + + .probe = mal_probe, + .remove = mal_remove, +}; + +static int __init init_mals(void) +{ + int rc; + + rc = ocp_register_driver(&mal_driver); + if (rc < 0) { + ocp_unregister_driver(&mal_driver); + return -ENODEV; + } + + return 0; +} + +static void __exit exit_mals(void) +{ + ocp_unregister_driver(&mal_driver); +} + +module_init(init_mals); +module_exit(exit_mals); diff --git a/drivers/net/ibm_emac/ibm_emac_mal.h b/drivers/net/ibm_emac/ibm_emac_mal.h new file mode 100644 index 000000000..8e456ce5a --- /dev/null +++ b/drivers/net/ibm_emac/ibm_emac_mal.h @@ -0,0 +1,130 @@ +#ifndef _IBM_EMAC_MAL_H +#define _IBM_EMAC_MAL_H + +#include + +#define MAL_DT_ALIGN (4096) /* Alignment for each channel's descriptor table */ + +#define MAL_CHAN_MASK(chan) (0x80000000 >> (chan)) + +/* MAL Buffer Descriptor structure */ +struct mal_descriptor { + unsigned short ctrl; /* MAL / Commac status control bits */ + short data_len; /* Max length is 4K-1 (12 bits) */ + unsigned char *data_ptr; /* pointer to actual data buffer */ +} __attribute__ ((packed)); + +/* the following defines are for the MadMAL status and control registers. */ +/* MADMAL transmit and receive status/control bits */ +#define MAL_RX_CTRL_EMPTY 0x8000 +#define MAL_RX_CTRL_WRAP 0x4000 +#define MAL_RX_CTRL_CM 0x2000 +#define MAL_RX_CTRL_LAST 0x1000 +#define MAL_RX_CTRL_FIRST 0x0800 +#define MAL_RX_CTRL_INTR 0x0400 + +#define MAL_TX_CTRL_READY 0x8000 +#define MAL_TX_CTRL_WRAP 0x4000 +#define MAL_TX_CTRL_CM 0x2000 +#define MAL_TX_CTRL_LAST 0x1000 +#define MAL_TX_CTRL_INTR 0x0400 + +struct mal_commac_ops { + void (*txeob) (void *dev, u32 chanmask); + void (*txde) (void *dev, u32 chanmask); + void (*rxeob) (void *dev, u32 chanmask); + void (*rxde) (void *dev, u32 chanmask); +}; + +struct mal_commac { + struct mal_commac_ops *ops; + void *dev; + u32 tx_chan_mask, rx_chan_mask; + struct list_head list; +}; + +struct ibm_ocp_mal { + int dcrbase; + + struct list_head commac; + u32 tx_chan_mask, rx_chan_mask; + + dma_addr_t tx_phys_addr; + struct mal_descriptor *tx_virt_addr; + + dma_addr_t rx_phys_addr; + struct mal_descriptor *rx_virt_addr; +}; + +#define GET_MAL_STANZA(base,dcrn) \ + case base: \ + x = mfdcr(dcrn(base)); \ + break; + +#define SET_MAL_STANZA(base,dcrn, val) \ + case base: \ + mtdcr(dcrn(base), (val)); \ + break; + +#define GET_MAL0_STANZA(dcrn) GET_MAL_STANZA(DCRN_MAL_BASE,dcrn) +#define SET_MAL0_STANZA(dcrn,val) SET_MAL_STANZA(DCRN_MAL_BASE,dcrn,val) + +#ifdef DCRN_MAL1_BASE +#define GET_MAL1_STANZA(dcrn) GET_MAL_STANZA(DCRN_MAL1_BASE,dcrn) +#define SET_MAL1_STANZA(dcrn,val) SET_MAL_STANZA(DCRN_MAL1_BASE,dcrn,val) +#else /* ! DCRN_MAL1_BASE */ +#define GET_MAL1_STANZA(dcrn) +#define SET_MAL1_STANZA(dcrn,val) +#endif + +#define get_mal_dcrn(mal, dcrn) ({ \ + u32 x; \ + switch ((mal)->dcrbase) { \ + GET_MAL0_STANZA(dcrn) \ + GET_MAL1_STANZA(dcrn) \ + default: \ + BUG(); \ + } \ +x; }) + +#define set_mal_dcrn(mal, dcrn, val) do { \ + switch ((mal)->dcrbase) { \ + SET_MAL0_STANZA(dcrn,val) \ + SET_MAL1_STANZA(dcrn,val) \ + default: \ + BUG(); \ + } } while (0) + +static inline void mal_enable_tx_channels(struct ibm_ocp_mal *mal, u32 chanmask) +{ + set_mal_dcrn(mal, DCRN_MALTXCASR, + get_mal_dcrn(mal, DCRN_MALTXCASR) | chanmask); +} + +static inline void mal_disable_tx_channels(struct ibm_ocp_mal *mal, + u32 chanmask) +{ + set_mal_dcrn(mal, DCRN_MALTXCARR, chanmask); +} + +static inline void mal_enable_rx_channels(struct ibm_ocp_mal *mal, u32 chanmask) +{ + set_mal_dcrn(mal, DCRN_MALRXCASR, + get_mal_dcrn(mal, DCRN_MALRXCASR) | chanmask); +} + +static inline void mal_disable_rx_channels(struct ibm_ocp_mal *mal, + u32 chanmask) +{ + set_mal_dcrn(mal, DCRN_MALRXCARR, chanmask); +} + +extern int mal_register_commac(struct ibm_ocp_mal *mal, + struct mal_commac *commac); +extern int mal_unregister_commac(struct ibm_ocp_mal *mal, + struct mal_commac *commac); + +extern int mal_set_rcbs(struct ibm_ocp_mal *mal, int channel, + unsigned long size); + +#endif /* _IBM_EMAC_MAL_H */ diff --git a/drivers/net/ibm_emac/ibm_emac_phy.c b/drivers/net/ibm_emac/ibm_emac_phy.c new file mode 100644 index 000000000..b439087df --- /dev/null +++ b/drivers/net/ibm_emac/ibm_emac_phy.c @@ -0,0 +1,297 @@ +/* + * ibm_ocp_phy.c + * + * PHY drivers for the ibm ocp ethernet driver. Borrowed + * from sungem_phy.c, though I only kept the generic MII + * driver for now. + * + * This file should be shared with other drivers or eventually + * merged as the "low level" part of miilib + * + * (c) 2003, Benjamin Herrenscmidt (benh@kernel.crashing.org) + * + */ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ibm_emac_phy.h" + +static int reset_one_mii_phy(struct mii_phy *phy, int phy_id) +{ + u16 val; + int limit = 10000; + + val = __phy_read(phy, phy_id, MII_BMCR); + val &= ~BMCR_ISOLATE; + val |= BMCR_RESET; + __phy_write(phy, phy_id, MII_BMCR, val); + + udelay(100); + + while (limit--) { + val = __phy_read(phy, phy_id, MII_BMCR); + if ((val & BMCR_RESET) == 0) + break; + udelay(10); + } + if ((val & BMCR_ISOLATE) && limit > 0) + __phy_write(phy, phy_id, MII_BMCR, val & ~BMCR_ISOLATE); + + return (limit <= 0); +} + +static int cis8201_init(struct mii_phy *phy) +{ + u16 epcr; + + epcr = phy_read(phy, MII_CIS8201_EPCR); + epcr &= ~EPCR_MODE_MASK; + + switch (phy->mode) { + case PHY_MODE_TBI: + epcr |= EPCR_TBI_MODE; + break; + case PHY_MODE_RTBI: + epcr |= EPCR_RTBI_MODE; + break; + case PHY_MODE_GMII: + epcr |= EPCR_GMII_MODE; + break; + case PHY_MODE_RGMII: + default: + epcr |= EPCR_RGMII_MODE; + } + + phy_write(phy, MII_CIS8201_EPCR, epcr); + + return 0; +} + +static int genmii_setup_aneg(struct mii_phy *phy, u32 advertise) +{ + u16 ctl, adv; + + phy->autoneg = 1; + phy->speed = SPEED_10; + phy->duplex = DUPLEX_HALF; + phy->pause = 0; + phy->advertising = advertise; + + /* Setup standard advertise */ + adv = phy_read(phy, MII_ADVERTISE); + adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4); + if (advertise & ADVERTISED_10baseT_Half) + adv |= ADVERTISE_10HALF; + if (advertise & ADVERTISED_10baseT_Full) + adv |= ADVERTISE_10FULL; + if (advertise & ADVERTISED_100baseT_Half) + adv |= ADVERTISE_100HALF; + if (advertise & ADVERTISED_100baseT_Full) + adv |= ADVERTISE_100FULL; + phy_write(phy, MII_ADVERTISE, adv); + + /* Start/Restart aneg */ + ctl = phy_read(phy, MII_BMCR); + ctl |= (BMCR_ANENABLE | BMCR_ANRESTART); + phy_write(phy, MII_BMCR, ctl); + + return 0; +} + +static int genmii_setup_forced(struct mii_phy *phy, int speed, int fd) +{ + u16 ctl; + + phy->autoneg = 0; + phy->speed = speed; + phy->duplex = fd; + phy->pause = 0; + + ctl = phy_read(phy, MII_BMCR); + ctl &= ~(BMCR_FULLDPLX | BMCR_SPEED100 | BMCR_ANENABLE); + + /* First reset the PHY */ + phy_write(phy, MII_BMCR, ctl | BMCR_RESET); + + /* Select speed & duplex */ + switch (speed) { + case SPEED_10: + break; + case SPEED_100: + ctl |= BMCR_SPEED100; + break; + case SPEED_1000: + default: + return -EINVAL; + } + if (fd == DUPLEX_FULL) + ctl |= BMCR_FULLDPLX; + phy_write(phy, MII_BMCR, ctl); + + return 0; +} + +static int genmii_poll_link(struct mii_phy *phy) +{ + u16 status; + + (void)phy_read(phy, MII_BMSR); + status = phy_read(phy, MII_BMSR); + if ((status & BMSR_LSTATUS) == 0) + return 0; + if (phy->autoneg && !(status & BMSR_ANEGCOMPLETE)) + return 0; + return 1; +} + +#define MII_CIS8201_ACSR 0x1c +#define ACSR_DUPLEX_STATUS 0x0020 +#define ACSR_SPEED_1000BASET 0x0010 +#define ACSR_SPEED_100BASET 0x0008 + +static int cis8201_read_link(struct mii_phy *phy) +{ + u16 acsr; + + if (phy->autoneg) { + acsr = phy_read(phy, MII_CIS8201_ACSR); + + if (acsr & ACSR_DUPLEX_STATUS) + phy->duplex = DUPLEX_FULL; + else + phy->duplex = DUPLEX_HALF; + if (acsr & ACSR_SPEED_1000BASET) { + phy->speed = SPEED_1000; + } else if (acsr & ACSR_SPEED_100BASET) + phy->speed = SPEED_100; + else + phy->speed = SPEED_10; + phy->pause = 0; + } + /* On non-aneg, we assume what we put in BMCR is the speed, + * though magic-aneg shouldn't prevent this case from occurring + */ + + return 0; +} + +static int genmii_read_link(struct mii_phy *phy) +{ + u16 lpa; + + if (phy->autoneg) { + lpa = phy_read(phy, MII_LPA); + + if (lpa & (LPA_10FULL | LPA_100FULL)) + phy->duplex = DUPLEX_FULL; + else + phy->duplex = DUPLEX_HALF; + if (lpa & (LPA_100FULL | LPA_100HALF)) + phy->speed = SPEED_100; + else + phy->speed = SPEED_10; + phy->pause = 0; + } + /* On non-aneg, we assume what we put in BMCR is the speed, + * though magic-aneg shouldn't prevent this case from occurring + */ + + return 0; +} + +#define MII_BASIC_FEATURES (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | \ + SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | \ + SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII) +#define MII_GBIT_FEATURES (MII_BASIC_FEATURES | \ + SUPPORTED_1000baseT_Half | SUPPORTED_1000baseT_Full) + +/* CIS8201 phy ops */ +static struct mii_phy_ops cis8201_phy_ops = { + init:cis8201_init, + setup_aneg:genmii_setup_aneg, + setup_forced:genmii_setup_forced, + poll_link:genmii_poll_link, + read_link:cis8201_read_link +}; + +/* Generic implementation for most 10/100 PHYs */ +static struct mii_phy_ops generic_phy_ops = { + setup_aneg:genmii_setup_aneg, + setup_forced:genmii_setup_forced, + poll_link:genmii_poll_link, + read_link:genmii_read_link +}; + +static struct mii_phy_def cis8201_phy_def = { + phy_id:0x000fc410, + phy_id_mask:0x000ffff0, + name:"CIS8201 Gigabit Ethernet", + features:MII_GBIT_FEATURES, + magic_aneg:0, + ops:&cis8201_phy_ops +}; + +static struct mii_phy_def genmii_phy_def = { + phy_id:0x00000000, + phy_id_mask:0x00000000, + name:"Generic MII", + features:MII_BASIC_FEATURES, + magic_aneg:0, + ops:&generic_phy_ops +}; + +static struct mii_phy_def *mii_phy_table[] = { + &cis8201_phy_def, + &genmii_phy_def, + NULL +}; + +int mii_phy_probe(struct mii_phy *phy, int mii_id) +{ + int rc; + u32 id; + struct mii_phy_def *def; + int i; + + phy->autoneg = 0; + phy->advertising = 0; + phy->mii_id = mii_id; + phy->speed = 0; + phy->duplex = 0; + phy->pause = 0; + + /* Take PHY out of isloate mode and reset it. */ + rc = reset_one_mii_phy(phy, mii_id); + if (rc) + return -ENODEV; + + /* Read ID and find matching entry */ + id = (phy_read(phy, MII_PHYSID1) << 16 | phy_read(phy, MII_PHYSID2)) + & 0xfffffff0; + for (i = 0; (def = mii_phy_table[i]) != NULL; i++) + if ((id & def->phy_id_mask) == def->phy_id) + break; + /* Should never be NULL (we have a generic entry), but... */ + if (def == NULL) + return -ENODEV; + + phy->def = def; + + /* Setup default advertising */ + phy->advertising = def->features; + + return 0; +} + +MODULE_LICENSE("GPL"); diff --git a/drivers/net/ibm_emac/ibm_emac_rgmii.h b/drivers/net/ibm_emac/ibm_emac_rgmii.h new file mode 100644 index 000000000..49f188f4e --- /dev/null +++ b/drivers/net/ibm_emac/ibm_emac_rgmii.h @@ -0,0 +1,65 @@ +/* + * Defines for the IBM RGMII bridge + * + * Based on ocp_zmii.h/ibm_emac_zmii.h + * Armin Kuster akuster@mvista.com + * + * Copyright 2004 MontaVista Software, Inc. + * Matt Porter + * + * 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. + */ + +#ifndef _IBM_EMAC_RGMII_H_ +#define _IBM_EMAC_RGMII_H_ + +#include + +/* RGMII bridge */ +typedef struct rgmii_regs { + u32 fer; /* Function enable register */ + u32 ssr; /* Speed select register */ +} rgmii_t; + +#define RGMII_INPUTS 4 + +/* RGMII device */ +struct ibm_ocp_rgmii { + struct rgmii_regs *base; + int mode[RGMII_INPUTS]; + int users; /* number of EMACs using this RGMII bridge */ +}; + +/* Fuctional Enable Reg */ +#define RGMII_FER_MASK(x) (0x00000007 << (4*x)) +#define RGMII_RTBI 0x00000004 +#define RGMII_RGMII 0x00000005 +#define RGMII_TBI 0x00000006 +#define RGMII_GMII 0x00000007 + +/* Speed Selection reg */ + +#define RGMII_SP2_100 0x00000002 +#define RGMII_SP2_1000 0x00000004 +#define RGMII_SP3_100 0x00000200 +#define RGMII_SP3_1000 0x00000400 + +#define RGMII_MII2_SPDMASK 0x00000007 +#define RGMII_MII3_SPDMASK 0x00000700 + +#define RGMII_MII2_100MB RGMII_SP2_100 & ~RGMII_SP2_1000 +#define RGMII_MII2_1000MB RGMII_SP2_1000 & ~RGMII_SP2_100 +#define RGMII_MII2_10MB ~(RGMII_SP2_100 | RGMII_SP2_1000) +#define RGMII_MII3_100MB RGMII_SP3_100 & ~RGMII_SP3_1000 +#define RGMII_MII3_1000MB RGMII_SP3_1000 & ~RGMII_SP3_100 +#define RGMII_MII3_10MB ~(RGMII_SP3_100 | RGMII_SP3_1000) + +#define RTBI 0 +#define RGMII 1 +#define TBI 2 +#define GMII 3 + +#endif /* _IBM_EMAC_RGMII_H_ */ diff --git a/drivers/net/ibm_emac/ibm_emac_tah.h b/drivers/net/ibm_emac/ibm_emac_tah.h new file mode 100644 index 000000000..ecfc69805 --- /dev/null +++ b/drivers/net/ibm_emac/ibm_emac_tah.h @@ -0,0 +1,48 @@ +/* + * Defines for the IBM TAH + * + * Copyright 2004 MontaVista Software, Inc. + * Matt Porter + * + * 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. + */ + +#ifndef _IBM_EMAC_TAH_H +#define _IBM_EMAC_TAH_H + +/* TAH */ +typedef struct tah_regs { + u32 tah_revid; + u32 pad[3]; + u32 tah_mr; + u32 tah_ssr0; + u32 tah_ssr1; + u32 tah_ssr2; + u32 tah_ssr3; + u32 tah_ssr4; + u32 tah_ssr5; + u32 tah_tsr; +} tah_t; + +/* TAH engine */ +#define TAH_MR_CVR 0x80000000 +#define TAH_MR_SR 0x40000000 +#define TAH_MR_ST_256 0x01000000 +#define TAH_MR_ST_512 0x02000000 +#define TAH_MR_ST_768 0x03000000 +#define TAH_MR_ST_1024 0x04000000 +#define TAH_MR_ST_1280 0x05000000 +#define TAH_MR_ST_1536 0x06000000 +#define TAH_MR_TFS_16KB 0x00000000 +#define TAH_MR_TFS_2KB 0x00200000 +#define TAH_MR_TFS_4KB 0x00400000 +#define TAH_MR_TFS_6KB 0x00600000 +#define TAH_MR_TFS_8KB 0x00800000 +#define TAH_MR_TFS_10KB 0x00a00000 +#define TAH_MR_DTFP 0x00100000 +#define TAH_MR_DIG 0x00080000 + +#endif /* _IBM_EMAC_TAH_H */ diff --git a/drivers/net/ibm_emac/ibm_emac_zmii.h b/drivers/net/ibm_emac/ibm_emac_zmii.h new file mode 100644 index 000000000..6f6cd2a39 --- /dev/null +++ b/drivers/net/ibm_emac/ibm_emac_zmii.h @@ -0,0 +1,93 @@ +/* + * ocp_zmii.h + * + * Defines for the IBM ZMII bridge + * + * Armin Kuster akuster@mvista.com + * Dec, 2001 + * + * Copyright 2001 MontaVista Softare Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef _IBM_EMAC_ZMII_H_ +#define _IBM_EMAC_ZMII_H_ + +#include + +/* ZMII bridge registers */ +struct zmii_regs { + u32 fer; /* Function enable reg */ + u32 ssr; /* Speed select reg */ + u32 smiirs; /* SMII status reg */ +}; + +#define ZMII_INPUTS 4 + +/* ZMII device */ +struct ibm_ocp_zmii { + struct zmii_regs *base; + int mode[ZMII_INPUTS]; + int users; /* number of EMACs using this ZMII bridge */ +}; + +/* Fuctional Enable Reg */ + +#define ZMII_FER_MASK(x) (0xf0000000 >> (4*x)) + +#define ZMII_MDI0 0x80000000 +#define ZMII_SMII0 0x40000000 +#define ZMII_RMII0 0x20000000 +#define ZMII_MII0 0x10000000 +#define ZMII_MDI1 0x08000000 +#define ZMII_SMII1 0x04000000 +#define ZMII_RMII1 0x02000000 +#define ZMII_MII1 0x01000000 +#define ZMII_MDI2 0x00800000 +#define ZMII_SMII2 0x00400000 +#define ZMII_RMII2 0x00200000 +#define ZMII_MII2 0x00100000 +#define ZMII_MDI3 0x00080000 +#define ZMII_SMII3 0x00040000 +#define ZMII_RMII3 0x00020000 +#define ZMII_MII3 0x00010000 + +/* Speed Selection reg */ + +#define ZMII_SCI0 0x40000000 +#define ZMII_FSS0 0x20000000 +#define ZMII_SP0 0x10000000 +#define ZMII_SCI1 0x04000000 +#define ZMII_FSS1 0x02000000 +#define ZMII_SP1 0x01000000 +#define ZMII_SCI2 0x00400000 +#define ZMII_FSS2 0x00200000 +#define ZMII_SP2 0x00100000 +#define ZMII_SCI3 0x00040000 +#define ZMII_FSS3 0x00020000 +#define ZMII_SP3 0x00010000 + +#define ZMII_MII0_100MB ZMII_SP0 +#define ZMII_MII0_10MB ~ZMII_SP0 +#define ZMII_MII1_100MB ZMII_SP1 +#define ZMII_MII1_10MB ~ZMII_SP1 +#define ZMII_MII2_100MB ZMII_SP2 +#define ZMII_MII2_10MB ~ZMII_SP2 +#define ZMII_MII3_100MB ZMII_SP3 +#define ZMII_MII3_10MB ~ZMII_SP3 + +/* SMII Status reg */ + +#define ZMII_STS0 0xFF000000 /* EMAC0 smii status mask */ +#define ZMII_STS1 0x00FF0000 /* EMAC1 smii status mask */ + +#define SMII 0 +#define RMII 1 +#define MII 2 +#define MDI 3 + +#endif /* _IBM_EMAC_ZMII_H_ */ diff --git a/drivers/net/ne-h8300.c b/drivers/net/ne-h8300.c new file mode 100644 index 000000000..86f34b5ec --- /dev/null +++ b/drivers/net/ne-h8300.c @@ -0,0 +1,666 @@ +/* ne-h8300.c: A NE2000 clone on H8/300 driver for linux. */ +/* + original ne.c + Written 1992-94 by Donald Becker. + + Copyright 1993 United States Government as represented by the + Director, National Security Agency. + + This software may be used and distributed according to the terms + of the GNU General Public License, incorporated herein by reference. + + The author may be reached as becker@scyld.com, or C/O + Scyld Computing Corporation, 410 Severn Ave., Suite 210, Annapolis MD 21403 + + H8/300 modified + Yoshinori Sato +*/ + +static const char version1[] = +"ne-h8300.c:v1.00 2004/04/11 ysato\n"; + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "8390.h" + +/* Some defines that people can play with if so inclined. */ + +/* Do we perform extra sanity checks on stuff ? */ +/* #define NE_SANITY_CHECK */ + +/* Do we implement the read before write bugfix ? */ +/* #define NE_RW_BUGFIX */ + +/* Do we have a non std. amount of memory? (in units of 256 byte pages) */ +/* #define PACKETBUF_MEMSIZE 0x40 */ + +/* A zero-terminated list of I/O addresses to be probed at boot. */ + +/* ---- No user-serviceable parts below ---- */ + +#define NE_BASE (dev->base_addr) +#define NE_CMD 0x00 +#define NE_DATAPORT (ei_status.word16?0x20:0x10) /* NatSemi-defined port window offset. */ +#define NE_RESET (ei_status.word16?0x3f:0x1f) /* Issue a read to reset, a write to clear. */ +#define NE_IO_EXTENT (ei_status.word16?0x40:0x20) + +#define NESM_START_PG 0x40 /* First page of TX buffer */ +#define NESM_STOP_PG 0x80 /* Last page +1 of RX ring */ + +static int ne_probe1(struct net_device *dev, int ioaddr); + +static int ne_open(struct net_device *dev); +static int ne_close(struct net_device *dev); + +static void ne_reset_8390(struct net_device *dev); +static void ne_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, + int ring_page); +static void ne_block_input(struct net_device *dev, int count, + struct sk_buff *skb, int ring_offset); +static void ne_block_output(struct net_device *dev, const int count, + const unsigned char *buf, const int start_page); + + +static u32 reg_offset[16]; + +static int __init init_reg_offset(struct net_device *dev,unsigned long base_addr) +{ + struct ei_device *ei_local = (struct ei_device *) netdev_priv(dev); + int i; + unsigned char bus_width; + + bus_width = *(volatile unsigned char *)ABWCR; + bus_width &= 1 << ((base_addr >> 21) & 7); + + for (i = 0; i < sizeof(reg_offset) / sizeof(u32); i++) + if (bus_width == 0) + reg_offset[i] = i * 2 + 1; + else + reg_offset[i] = i; + + ei_local->reg_offset = reg_offset; + return 0; +} + +static int __initdata h8300_ne_count = 0; +#ifdef CONFIG_H8300H_H8MAX +static unsigned long __initdata h8300_ne_base[] = { 0x800600 }; +static int h8300_ne_irq[] = {EXT_IRQ4}; +#endif +#ifdef CONFIG_H8300H_AKI3068NET +static unsigned long __initdata h8300_ne_base[] = { 0x200000 }; +static int h8300_ne_irq[] = {EXT_IRQ5}; +#endif + +static inline int init_dev(struct net_device *dev) +{ + if (h8300_ne_count < (sizeof(h8300_ne_base) / sizeof(unsigned long))) { + dev->base_addr = h8300_ne_base[h8300_ne_count]; + dev->irq = h8300_ne_irq[h8300_ne_count]; + h8300_ne_count++; + return 0; + } else + return -ENODEV; +} + +/* Probe for various non-shared-memory ethercards. + + NEx000-clone boards have a Station Address PROM (SAPROM) in the packet + buffer memory space. NE2000 clones have 0x57,0x57 in bytes 0x0e,0x0f of + the SAPROM, while other supposed NE2000 clones must be detected by their + SA prefix. + + Reading the SAPROM from a word-wide card with the 8390 set in byte-wide + mode results in doubled values, which can be detected and compensated for. + + The probe is also responsible for initializing the card and filling + in the 'dev' and 'ei_status' structures. + + We use the minimum memory size for some ethercard product lines, iff we can't + distinguish models. You can increase the packet buffer size by setting + PACKETBUF_MEMSIZE. Reported Cabletron packet buffer locations are: + E1010 starts at 0x100 and ends at 0x2000. + E1010-x starts at 0x100 and ends at 0x8000. ("-x" means "more memory") + E2010 starts at 0x100 and ends at 0x4000. + E2010-x starts at 0x100 and ends at 0xffff. */ + +static int __init do_ne_probe(struct net_device *dev) +{ + unsigned int base_addr = dev->base_addr; + + SET_MODULE_OWNER(dev); + + /* First check any supplied i/o locations. User knows best. */ + if (base_addr > 0x1ff) /* Check a single specified location. */ + return ne_probe1(dev, base_addr); + else if (base_addr != 0) /* Don't probe at all. */ + return -ENXIO; + + return -ENODEV; +} + +static void cleanup_card(struct net_device *dev) +{ + free_irq(dev->irq, dev); + release_region(dev->base_addr, NE_IO_EXTENT); +} + +struct net_device * __init ne_probe(int unit) +{ + struct net_device *dev = alloc_ei_netdev(); + int err; + + if (!dev) + return ERR_PTR(-ENOMEM); + + if (init_dev(dev)) + return ERR_PTR(-ENODEV); + + sprintf(dev->name, "eth%d", unit); + netdev_boot_setup_check(dev); + + err = init_reg_offset(dev, dev->base_addr); + if (err) + goto out; + + err = do_ne_probe(dev); + if (err) + goto out; + err = register_netdev(dev); + if (err) + goto out1; + return dev; +out1: + cleanup_card(dev); +out: + free_netdev(dev); + return ERR_PTR(err); +} + +static int __init ne_probe1(struct net_device *dev, int ioaddr) +{ + int i; + unsigned char SA_prom[16]; + int wordlength = 2; + const char *name = NULL; + int start_page, stop_page; + int reg0, ret; + static unsigned version_printed; + struct ei_device *ei_local = (struct ei_device *) netdev_priv(dev); + unsigned char bus_width; + + if (!request_region(ioaddr, NE_IO_EXTENT, dev->name)) + return -EBUSY; + + reg0 = inb_p(ioaddr); + if (reg0 == 0xFF) { + ret = -ENODEV; + goto err_out; + } + + /* Do a preliminary verification that we have a 8390. */ + { + int regd; + outb_p(E8390_NODMA+E8390_PAGE1+E8390_STOP, ioaddr + E8390_CMD); + regd = inb_p(ioaddr + EI_SHIFT(0x0d)); + outb_p(0xff, ioaddr + EI_SHIFT(0x0d)); + outb_p(E8390_NODMA+E8390_PAGE0, ioaddr + E8390_CMD); + inb_p(ioaddr + EN0_COUNTER0); /* Clear the counter by reading. */ + if (inb_p(ioaddr + EN0_COUNTER0) != 0) { + outb_p(reg0, ioaddr + EI_SHIFT(0)); + outb_p(regd, ioaddr + EI_SHIFT(0x0d)); /* Restore the old values. */ + ret = -ENODEV; + goto err_out; + } + } + + if (ei_debug && version_printed++ == 0) + printk(KERN_INFO "%s", version1); + + printk(KERN_INFO "NE*000 ethercard probe at %08x:", ioaddr); + + /* Read the 16 bytes of station address PROM. + We must first initialize registers, similar to NS8390_init(eifdev, 0). + We can't reliably read the SAPROM address without this. + (I learned the hard way!). */ + { + struct {unsigned char value, offset; } program_seq[] = + { + {E8390_NODMA+E8390_PAGE0+E8390_STOP, E8390_CMD}, /* Select page 0*/ + {0x48, EN0_DCFG}, /* Set byte-wide (0x48) access. */ + {0x00, EN0_RCNTLO}, /* Clear the count regs. */ + {0x00, EN0_RCNTHI}, + {0x00, EN0_IMR}, /* Mask completion irq. */ + {0xFF, EN0_ISR}, + {E8390_RXOFF, EN0_RXCR}, /* 0x20 Set to monitor */ + {E8390_TXOFF, EN0_TXCR}, /* 0x02 and loopback mode. */ + {32, EN0_RCNTLO}, + {0x00, EN0_RCNTHI}, + {0x00, EN0_RSARLO}, /* DMA starting at 0x0000. */ + {0x00, EN0_RSARHI}, + {E8390_RREAD+E8390_START, E8390_CMD}, + }; + + for (i = 0; i < sizeof(program_seq)/sizeof(program_seq[0]); i++) + outb_p(program_seq[i].value, ioaddr + program_seq[i].offset); + + } + bus_width = *(volatile unsigned char *)ABWCR; + bus_width &= 1 << ((ioaddr >> 21) & 7); + ei_status.word16 = (bus_width == 0); /* temporary setting */ + for(i = 0; i < 16 /*sizeof(SA_prom)*/; i++) { + SA_prom[i] = inb_p(ioaddr + NE_DATAPORT); + inb_p(ioaddr + NE_DATAPORT); /* dummy read */ + } + + start_page = NESM_START_PG; + stop_page = NESM_STOP_PG; + + if (bus_width) + wordlength = 1; + else + outb_p(0x49, ioaddr + EN0_DCFG); + + /* Set up the rest of the parameters. */ + name = (wordlength == 2) ? "NE2000" : "NE1000"; + + if (! dev->irq) { + printk(" failed to detect IRQ line.\n"); + ret = -EAGAIN; + goto err_out; + } + + /* Snarf the interrupt now. There's no point in waiting since we cannot + share and the board will usually be enabled. */ + ret = request_irq(dev->irq, ei_interrupt, 0, name, dev); + if (ret) { + printk (" unable to get IRQ %d (errno=%d).\n", dev->irq, ret); + goto err_out; + } + + dev->base_addr = ioaddr; + + for(i = 0; i < ETHER_ADDR_LEN; i++) { + printk(" %2.2x", SA_prom[i]); + dev->dev_addr[i] = SA_prom[i]; + } + + printk("\n%s: %s found at %#x, using IRQ %d.\n", + dev->name, name, ioaddr, dev->irq); + + ei_status.name = name; + ei_status.tx_start_page = start_page; + ei_status.stop_page = stop_page; + ei_status.word16 = (wordlength == 2); + + ei_status.rx_start_page = start_page + TX_PAGES; +#ifdef PACKETBUF_MEMSIZE + /* Allow the packet buffer size to be overridden by know-it-alls. */ + ei_status.stop_page = ei_status.tx_start_page + PACKETBUF_MEMSIZE; +#endif + + ei_status.reset_8390 = &ne_reset_8390; + ei_status.block_input = &ne_block_input; + ei_status.block_output = &ne_block_output; + ei_status.get_8390_hdr = &ne_get_8390_hdr; + ei_status.priv = 0; + dev->open = &ne_open; + dev->stop = &ne_close; +#ifdef CONFIG_NET_POLL_CONTROLLER + dev->poll_controller = ei_poll; +#endif + NS8390_init(dev, 0); + return 0; + +err_out: + release_region(ioaddr, NE_IO_EXTENT); + return ret; +} + +static int ne_open(struct net_device *dev) +{ + ei_open(dev); + return 0; +} + +static int ne_close(struct net_device *dev) +{ + if (ei_debug > 1) + printk(KERN_DEBUG "%s: Shutting down ethercard.\n", dev->name); + ei_close(dev); + return 0; +} + +/* Hard reset the card. This used to pause for the same period that a + 8390 reset command required, but that shouldn't be necessary. */ + +static void ne_reset_8390(struct net_device *dev) +{ + unsigned long reset_start_time = jiffies; + struct ei_device *ei_local = (struct ei_device *) netdev_priv(dev); + + if (ei_debug > 1) + printk(KERN_DEBUG "resetting the 8390 t=%ld...", jiffies); + + /* DON'T change these to inb_p/outb_p or reset will fail on clones. */ + outb(inb(NE_BASE + NE_RESET), NE_BASE + NE_RESET); + + ei_status.txing = 0; + ei_status.dmaing = 0; + + /* This check _should_not_ be necessary, omit eventually. */ + while ((inb_p(NE_BASE+EN0_ISR) & ENISR_RESET) == 0) + if (jiffies - reset_start_time > 2*HZ/100) { + printk(KERN_WARNING "%s: ne_reset_8390() did not complete.\n", dev->name); + break; + } + outb_p(ENISR_RESET, NE_BASE + EN0_ISR); /* Ack intr. */ +} + +/* Grab the 8390 specific header. Similar to the block_input routine, but + we don't need to be concerned with ring wrap as the header will be at + the start of a page, so we optimize accordingly. */ + +static void ne_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page) +{ + struct ei_device *ei_local = (struct ei_device *) netdev_priv(dev); + /* This *shouldn't* happen. If it does, it's the last thing you'll see */ + + if (ei_status.dmaing) + { + printk(KERN_EMERG "%s: DMAing conflict in ne_get_8390_hdr " + "[DMAstat:%d][irqlock:%d].\n", + dev->name, ei_status.dmaing, ei_status.irqlock); + return; + } + + ei_status.dmaing |= 0x01; + outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, NE_BASE + NE_CMD); + outb_p(sizeof(struct e8390_pkt_hdr), NE_BASE + EN0_RCNTLO); + outb_p(0, NE_BASE + EN0_RCNTHI); + outb_p(0, NE_BASE + EN0_RSARLO); /* On page boundary */ + outb_p(ring_page, NE_BASE + EN0_RSARHI); + outb_p(E8390_RREAD+E8390_START, NE_BASE + NE_CMD); + + if (ei_status.word16) { + int len; + unsigned short *p = (unsigned short *)hdr; + for (len = sizeof(struct e8390_pkt_hdr)>>1; len > 0; len--) + *p++ = inw(NE_BASE + NE_DATAPORT); + } else + insb(NE_BASE + NE_DATAPORT, hdr, sizeof(struct e8390_pkt_hdr)); + + outb_p(ENISR_RDC, NE_BASE + EN0_ISR); /* Ack intr. */ + ei_status.dmaing &= ~0x01; + + le16_to_cpus(&hdr->count); +} + +/* Block input and output, similar to the Crynwr packet driver. If you + are porting to a new ethercard, look at the packet driver source for hints. + The NEx000 doesn't share the on-board packet memory -- you have to put + the packet out through the "remote DMA" dataport using outb. */ + +static void ne_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset) +{ + struct ei_device *ei_local = (struct ei_device *) netdev_priv(dev); +#ifdef NE_SANITY_CHECK + int xfer_count = count; +#endif + char *buf = skb->data; + + /* This *shouldn't* happen. If it does, it's the last thing you'll see */ + if (ei_status.dmaing) + { + printk(KERN_EMERG "%s: DMAing conflict in ne_block_input " + "[DMAstat:%d][irqlock:%d].\n", + dev->name, ei_status.dmaing, ei_status.irqlock); + return; + } + ei_status.dmaing |= 0x01; + outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, NE_BASE + NE_CMD); + outb_p(count & 0xff, NE_BASE + EN0_RCNTLO); + outb_p(count >> 8, NE_BASE + EN0_RCNTHI); + outb_p(ring_offset & 0xff, NE_BASE + EN0_RSARLO); + outb_p(ring_offset >> 8, NE_BASE + EN0_RSARHI); + outb_p(E8390_RREAD+E8390_START, NE_BASE + NE_CMD); + if (ei_status.word16) + { + int len; + unsigned short *p = (unsigned short *)buf; + for (len = count>>1; len > 0; len--) + *p++ = inw(NE_BASE + NE_DATAPORT); + if (count & 0x01) + { + buf[count-1] = inb(NE_BASE + NE_DATAPORT); +#ifdef NE_SANITY_CHECK + xfer_count++; +#endif + } + } else { + insb(NE_BASE + NE_DATAPORT, buf, count); + } + +#ifdef NE_SANITY_CHECK + /* This was for the ALPHA version only, but enough people have + been encountering problems so it is still here. If you see + this message you either 1) have a slightly incompatible clone + or 2) have noise/speed problems with your bus. */ + + if (ei_debug > 1) + { + /* DMA termination address check... */ + int addr, tries = 20; + do { + /* DON'T check for 'inb_p(EN0_ISR) & ENISR_RDC' here + -- it's broken for Rx on some cards! */ + int high = inb_p(NE_BASE + EN0_RSARHI); + int low = inb_p(NE_BASE + EN0_RSARLO); + addr = (high << 8) + low; + if (((ring_offset + xfer_count) & 0xff) == low) + break; + } while (--tries > 0); + if (tries <= 0) + printk(KERN_WARNING "%s: RX transfer address mismatch," + "%#4.4x (expected) vs. %#4.4x (actual).\n", + dev->name, ring_offset + xfer_count, addr); + } +#endif + outb_p(ENISR_RDC, NE_BASE + EN0_ISR); /* Ack intr. */ + ei_status.dmaing &= ~0x01; +} + +static void ne_block_output(struct net_device *dev, int count, + const unsigned char *buf, const int start_page) +{ + struct ei_device *ei_local = (struct ei_device *) netdev_priv(dev); + unsigned long dma_start; +#ifdef NE_SANITY_CHECK + int retries = 0; +#endif + + /* Round the count up for word writes. Do we need to do this? + What effect will an odd byte count have on the 8390? + I should check someday. */ + + if (ei_status.word16 && (count & 0x01)) + count++; + + /* This *shouldn't* happen. If it does, it's the last thing you'll see */ + if (ei_status.dmaing) + { + printk(KERN_EMERG "%s: DMAing conflict in ne_block_output." + "[DMAstat:%d][irqlock:%d]\n", + dev->name, ei_status.dmaing, ei_status.irqlock); + return; + } + ei_status.dmaing |= 0x01; + /* We should already be in page 0, but to be safe... */ + outb_p(E8390_PAGE0+E8390_START+E8390_NODMA, NE_BASE + NE_CMD); + +#ifdef NE_SANITY_CHECK +retry: +#endif + +#ifdef NE8390_RW_BUGFIX + /* Handle the read-before-write bug the same way as the + Crynwr packet driver -- the NatSemi method doesn't work. + Actually this doesn't always work either, but if you have + problems with your NEx000 this is better than nothing! */ + + outb_p(0x42, NE_BASE + EN0_RCNTLO); + outb_p(0x00, NE_BASE + EN0_RCNTHI); + outb_p(0x42, NE_BASE + EN0_RSARLO); + outb_p(0x00, NE_BASE + EN0_RSARHI); + outb_p(E8390_RREAD+E8390_START, NE_BASE + NE_CMD); + /* Make certain that the dummy read has occurred. */ + udelay(6); +#endif + + outb_p(ENISR_RDC, NE_BASE + EN0_ISR); + + /* Now the normal output. */ + outb_p(count & 0xff, NE_BASE + EN0_RCNTLO); + outb_p(count >> 8, NE_BASE + EN0_RCNTHI); + outb_p(0x00, NE_BASE + EN0_RSARLO); + outb_p(start_page, NE_BASE + EN0_RSARHI); + + outb_p(E8390_RWRITE+E8390_START, NE_BASE + NE_CMD); + if (ei_status.word16) { + int len; + unsigned short *p = (unsigned short *)buf; + for (len = count>>1; len > 0; len--) + outw(*p++, NE_BASE + NE_DATAPORT); + } else { + outsb(NE_BASE + NE_DATAPORT, buf, count); + } + + dma_start = jiffies; + +#ifdef NE_SANITY_CHECK + /* This was for the ALPHA version only, but enough people have + been encountering problems so it is still here. */ + + if (ei_debug > 1) + { + /* DMA termination address check... */ + int addr, tries = 20; + do { + int high = inb_p(NE_BASE + EN0_RSARHI); + int low = inb_p(NE_BASE + EN0_RSARLO); + addr = (high << 8) + low; + if ((start_page << 8) + count == addr) + break; + } while (--tries > 0); + + if (tries <= 0) + { + printk(KERN_WARNING "%s: Tx packet transfer address mismatch," + "%#4.4x (expected) vs. %#4.4x (actual).\n", + dev->name, (start_page << 8) + count, addr); + if (retries++ == 0) + goto retry; + } + } +#endif + + while ((inb_p(NE_BASE + EN0_ISR) & ENISR_RDC) == 0) + if (jiffies - dma_start > 2*HZ/100) { /* 20ms */ + printk(KERN_WARNING "%s: timeout waiting for Tx RDC.\n", dev->name); + ne_reset_8390(dev); + NS8390_init(dev,1); + break; + } + + outb_p(ENISR_RDC, NE_BASE + EN0_ISR); /* Ack intr. */ + ei_status.dmaing &= ~0x01; + return; +} + + +#ifdef MODULE +#define MAX_NE_CARDS 1 /* Max number of NE cards per module */ +static struct net_device *dev_ne[MAX_NE_CARDS]; +static int io[MAX_NE_CARDS]; +static int irq[MAX_NE_CARDS]; +static int bad[MAX_NE_CARDS]; /* 0xbad = bad sig or no reset ack */ + +MODULE_PARM(io, "1-" __MODULE_STRING(MAX_NE_CARDS) "i"); +MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_NE_CARDS) "i"); +MODULE_PARM(bad, "1-" __MODULE_STRING(MAX_NE_CARDS) "i"); +MODULE_PARM_DESC(io, "I/O base address(es)"); +MODULE_PARM_DESC(irq, "IRQ number(s)"); +MODULE_DESCRIPTION("H8/300 NE2000 Ethernet driver"); +MODULE_LICENSE("GPL"); + +/* This is set up so that no ISA autoprobe takes place. We can't guarantee +that the ne2k probe is the last 8390 based probe to take place (as it +is at boot) and so the probe will get confused by any other 8390 cards. +ISA device autoprobes on a running machine are not recommended anyway. */ + +int init_module(void) +{ + int this_dev, found = 0; + int err; + + for (this_dev = 0; this_dev < MAX_NE_CARDS; this_dev++) { + struct net_device *dev = alloc_ei_netdev(); + if (!dev) + break; + if (io[this_dev]) { + dev->irq = irq[this_dev]; + dev->mem_end = bad[this_dev]; + dev->base_addr = io[this_dev]; + } else { + dev->base_addr = h8300_ne_base[this_dev]; + dev->irq = h8300_ne_irq[this_dev]; + } + err = init_reg_offset(dev, dev->base_addr); + if (!err) { + if (do_ne_probe(dev) == 0) { + if (register_netdev(dev) == 0) { + dev_ne[found++] = dev; + continue; + } + cleanup_card(dev); + } + } + free_netdev(dev); + if (found) + break; + if (io[this_dev] != 0) + printk(KERN_WARNING "ne.c: No NE*000 card found at i/o = %#x\n", dev->base_addr); + else + printk(KERN_NOTICE "ne.c: You must supply \"io=0xNNN\" value(s) for ISA cards.\n"); + return -ENXIO; + } + if (found) + return 0; + return -ENODEV; +} + +void cleanup_module(void) +{ + int this_dev; + + for (this_dev = 0; this_dev < MAX_NE_CARDS; this_dev++) { + struct net_device *dev = dev_ne[this_dev]; + if (dev) { + unregister_netdev(dev); + cleanup_card(dev); + free_netdev(dev); + } + } +} +#endif /* MODULE */ diff --git a/drivers/net/rcif.h b/drivers/net/rcif.h deleted file mode 100644 index 85ff8615c..000000000 --- a/drivers/net/rcif.h +++ /dev/null @@ -1,292 +0,0 @@ -/* -** ************************************************************************* -** -** -** R C I F . H -** -** -** RedCreek InterFace include file. -** -** --------------------------------------------------------------------- -** --- Copyright (c) 1998-1999, RedCreek Communications Inc. --- -** --- All rights reserved. --- -** --------------------------------------------------------------------- -** -** File Description: -** -** Header file private ioctl commands. -** -** -** This program is free software; you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation; either version 2 of the License, or -** (at your option) any later version. - -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. - -** You should have received a copy of the GNU General Public License -** along with this program; if not, write to the Free Software -** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -** ************************************************************************* -*/ - -#ifndef RCIF_H -#define RCIF_H - -/* The following protocol revision # should be incremented every time - a new protocol or new structures are used in this file. */ -int USER_PROTOCOL_REV = 2; /* used to track different protocol revisions */ - -/* define a single TCB & buffer */ -typedef struct { /* a single buffer */ - U32 context; /* context */ - U32 scount; /* segment count */ - U32 size; /* segment size */ - U32 addr; /* segment physical address */ -} __attribute__ ((packed)) - singleB, *psingleB; -typedef struct { /* a single TCB */ - /* - ** +-----------------------+ - ** | 1 | one buffer in the TCB - ** +-----------------------+ - ** | | user's buffer reference - ** +-----------------------+ - ** | 1 | one segment buffer - ** +-----------------------+ _ - ** | | size \ - ** +-----------------------+ \ segment descriptor - ** | | physical address of buffer / - ** +-----------------------+ _/ - */ - U32 bcount; /* buffer count */ - singleB b; /* buffer */ - -} __attribute__ ((packed)) - singleTCB, *psingleTCB; - -/* - When adding new entries, please add all 5 related changes, since - it helps keep everything consistent: - 1) User structure entry - 2) User data entry - 3) Structure short-cut entry - 4) Data short-cut entry - 5) Command identifier entry - - For Example ("GETSPEED"): - 1) struct RCgetspeed_tag { U32 LinkSpeedCode; } RCgetspeed; - 2) struct RCgetspeed_tag *getspeed; - 3) #define RCUS_GETSPEED data.RCgetspeed; - 4) #define RCUD_GETSPEED _RC_user_data.getspeed - 5) #define RCUC_GETSPEED 0x02 - - Notes for the "GETSPEED" entry, above: - 1) RCgetspeed - RC{name} - RCgetspeed_tag - RC{name}_tag - LinkSpeedCode - create any structure format desired (not too large, - since memory will be unioned with all other entries) - 2) RCgetspeed_tag - RC{name}_tag chosen in #1 - getspeed - arbitrary name (ptr to structure in #1) - 3) RCUS_GETSPEED - RCUS_{NAME} ("NAME" & "name" do not have to the same) - data.RCgetspeed - data.RC{name} ("RC{name}" from #1) - 4) RCUD_GETSPEED - _RC_user_data.getspeed ("getspeed" from #2) - 5) RCUC_GETSPEED - unique hex identifier entry. -*/ - -typedef struct RC_user_tag RCuser_struct; - -/* 1) User structure entry */ -struct RC_user_tag { - int cmd; - union { - /* GETINFO structure */ - struct RCgetinfo_tag { - unsigned long int mem_start; - unsigned long int mem_end; - unsigned long int base_addr; - unsigned char irq; - unsigned char dma; - unsigned char port; - } RCgetinfo; /* <---- RCgetinfo */ - - /* GETSPEED structure */ - struct RCgetspeed_tag { - U32 LinkSpeedCode; - } RCgetspeed; /* <---- RCgetspeed */ - - /* SETSPEED structure */ - struct RCsetspeed_tag { - U16 LinkSpeedCode; - } RCsetspeed; /* <---- RCsetspeed */ - - /* GETPROM structure */ - struct RCgetprom_tag { - U32 PromMode; - } RCgetprom; /* <---- RCgetprom */ - - /* SETPROM structure */ - struct RCsetprom_tag { - U16 PromMode; - } RCsetprom; /* <---- RCsetprom */ - - /* GETBROADCAST structure */ - struct RCgetbroadcast_tag { - U32 BroadcastMode; - } RCgetbroadcast; /* <---- RCgetbroadcast */ - - /* SETBROADCAST structure */ - struct RCsetbroadcast_tag { - U16 BroadcastMode; - } RCsetbroadcast; /* <---- RCsetbroadcast */ - - /* GETFIRMWAREVER structure */ -#define FirmStringLen 80 - struct RCgetfwver_tag { - U8 FirmString[FirmStringLen]; - } RCgetfwver; /* <---- RCgetfwver */ - - /* GETIPANDMASK structure */ - struct RCgetipnmask_tag { - U32 IpAddr; - U32 NetMask; - } RCgetipandmask; /* <---- RCgetipandmask */ - - /* SETIPANDMASK structure */ - struct RCsetipnmask_tag { - U32 IpAddr; - U32 NetMask; - } RCsetipandmask; /* <---- RCsetipandmask */ - - /* GETMAC structure */ -#define MAC_SIZE 10 - struct RCgetmac_tag { - U8 mac[MAC_SIZE]; - } RCgetmac; /* <---- RCgetmac */ - - /* SETMAC structure */ - struct RCsetmac_tag { - U8 mac[MAC_SIZE]; - } RCsetmac; /* <---- RCsetmac */ - - /* GETLINKSTATUS structure */ - struct RCgetlnkstatus_tag { - U32 ReturnStatus; - } RCgetlnkstatus; /* <---- RCgetlnkstatus */ - - /* GETLINKSTATISTICS structure */ - struct RCgetlinkstats_tag { - RCLINKSTATS StatsReturn; - } RCgetlinkstats; /* <---- RCgetlinkstats */ - - /* DEFAULT structure (when no command was recognized) */ - struct RCdefault_tag { - int rc; - } RCdefault; /* <---- RCdefault */ - - } data; - -}; /* struct RC_user_tag { ... } */ - -/* 2) User data entry */ -/* RCUD = RedCreek User Data */ -union RC_user_data_tag { /* structure tags used are taken from RC_user_tag structure above */ - struct RCgetinfo_tag *getinfo; - struct RCgetspeed_tag *getspeed; - struct RCgetprom_tag *getprom; - struct RCgetbroadcast_tag *getbroadcast; - struct RCgetfwver_tag *getfwver; - struct RCgetipnmask_tag *getipandmask; - struct RCgetmac_tag *getmac; - struct RCgetlnkstatus_tag *getlinkstatus; - struct RCgetlinkstats_tag *getlinkstatistics; - struct RCdefault_tag *rcdefault; - struct RCsetspeed_tag *setspeed; - struct RCsetprom_tag *setprom; - struct RCsetbroadcast_tag *setbroadcast; - struct RCsetipnmask_tag *setipandmask; - struct RCsetmac_tag *setmac; -} _RC_user_data; /* declare as a global, so the defines below will work */ - -/* 3) Structure short-cut entry */ -/* define structure short-cuts *//* structure names are taken from RC_user_tag structure above */ -#define RCUS_GETINFO data.RCgetinfo; -#define RCUS_GETSPEED data.RCgetspeed; -#define RCUS_GETPROM data.RCgetprom; -#define RCUS_GETBROADCAST data.RCgetbroadcast; -#define RCUS_GETFWVER data.RCgetfwver; -#define RCUS_GETIPANDMASK data.RCgetipandmask; -#define RCUS_GETMAC data.RCgetmac; -#define RCUS_GETLINKSTATUS data.RCgetlnkstatus; -#define RCUS_GETLINKSTATISTICS data.RCgetlinkstats; -#define RCUS_DEFAULT data.RCdefault; -#define RCUS_SETSPEED data.RCsetspeed; -#define RCUS_SETPROM data.RCsetprom; -#define RCUS_SETBROADCAST data.RCsetbroadcast; -#define RCUS_SETIPANDMASK data.RCsetipandmask; -#define RCUS_SETMAC data.RCsetmac; - -/* 4) Data short-cut entry */ -/* define data short-cuts *//* pointer names are from RC_user_data_tag union (just below RC_user_tag) */ -#define RCUD_GETINFO _RC_user_data.getinfo -#define RCUD_GETSPEED _RC_user_data.getspeed -#define RCUD_GETPROM _RC_user_data.getprom -#define RCUD_GETBROADCAST _RC_user_data.getbroadcast -#define RCUD_GETFWVER _RC_user_data.getfwver -#define RCUD_GETIPANDMASK _RC_user_data.getipandmask -#define RCUD_GETMAC _RC_user_data.getmac -#define RCUD_GETLINKSTATUS _RC_user_data.getlinkstatus -#define RCUD_GETLINKSTATISTICS _RC_user_data.getlinkstatistics -#define RCUD_DEFAULT _RC_user_data.rcdefault -#define RCUD_SETSPEED _RC_user_data.setspeed -#define RCUD_SETPROM _RC_user_data.setprom -#define RCUD_SETBROADCAST _RC_user_data.setbroadcast -#define RCUD_SETIPANDMASK _RC_user_data.setipandmask -#define RCUD_SETMAC _RC_user_data.setmac - -/* 5) Command identifier entry */ -/* define command identifiers */ -#define RCUC_GETINFO 0x01 -#define RCUC_GETSPEED 0x02 -#define RCUC_GETFWVER 0x03 -#define RCUC_GETIPANDMASK 0x04 -#define RCUC_GETMAC 0x05 -#define RCUC_GETLINKSTATUS 0x06 -#define RCUC_GETLINKSTATISTICS 0x07 -#define RCUC_GETPROM 0x14 -#define RCUC_GETBROADCAST 0x15 -#define RCUC_DEFAULT 0xff -#define RCUC_SETSPEED 0x08 -#define RCUC_SETIPANDMASK 0x09 -#define RCUC_SETMAC 0x0a -#define RCUC_SETPROM 0x16 -#define RCUC_SETBROADCAST 0x17 - -/* define ioctl commands to use, when talking to RC 45/PCI driver */ -#define RCU_PROTOCOL_REV SIOCDEVPRIVATE -#define RCU_COMMAND SIOCDEVPRIVATE+1 - -/* - Intended use for the above defines is shown below (GETINFO, as this example): - - RCuser_struct RCuser; // declare RCuser structure - struct ifreq ifr; // declare an interface request structure - - RCuser.cmd = RCUC_GETINFO; // set user command to GETINFO - ifr->ifr_data = (caddr_t) &RCuser; // set point to user structure - - sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); // get a socket - ioctl(sock, RCU_COMMAND, &ifr); // do ioctl on socket - - RCUD_GETINFO = &RCuser.RCUS_GETINFO; // set data pointer for GETINFO - - // print results - printf("memory 0x%lx-0x%lx, base address 0x%x, irq 0x%x\n", - RCUD_GETINFO->mem_start, RCUD_GETINFO->mem_end, - RCUD_GETINFO->base_addr, RCUD_GETINFO->irq); -*/ - -#endif /* RCIF_H */ diff --git a/drivers/net/rclanmtl.c b/drivers/net/rclanmtl.c deleted file mode 100644 index 14bd88ab2..000000000 --- a/drivers/net/rclanmtl.c +++ /dev/null @@ -1,2029 +0,0 @@ -/* -** ************************************************************************* -** -** -** R C L A N M T L . C $Revision: 6 $ -** -** -** RedCreek I2O LAN Message Transport Layer program module. -** -** --------------------------------------------------------------------- -** --- Copyright (c) 1997-1999, RedCreek Communications Inc. --- -** --- All rights reserved. --- -** --------------------------------------------------------------------- -** -** File Description: -** -** Host side I2O (Intelligent I/O) LAN message transport layer. -** -** This program is free software; you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation; either version 2 of the License, or -** (at your option) any later version. - -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. - -** You should have received a copy of the GNU General Public License -** along with this program; if not, write to the Free Software -** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -** -** 1998-1999, LAN API was modified and enhanced by Alice Hennessy. -** -** Sometime in 1997, LAN API was written from scratch by Wendell Nichols. -** ************************************************************************* -*/ - -#define DEBUG 1 - -#define RC_LINUX_MODULE -#include "rclanmtl.h" - - /* RedCreek LAN device Target ID */ -#define RC_LAN_TARGET_ID 0x10 - /* RedCreek's OSM default LAN receive Initiator */ -#define DEFAULT_RECV_INIT_CONTEXT 0xA17 - -/* -** I2O message structures -*/ - -#define I2O_TID_SZ 12 -#define I2O_FUNCTION_SZ 8 - -/* Transaction Reply Lists (TRL) Control Word structure */ - -#define I2O_TRL_FLAGS_SINGLE_FIXED_LENGTH 0x00 -#define I2O_TRL_FLAGS_SINGLE_VARIABLE_LENGTH 0x40 -#define I2O_TRL_FLAGS_MULTIPLE_FIXED_LENGTH 0x80 - -/* LAN Class specific functions */ - -#define I2O_LAN_PACKET_SEND 0x3B -#define I2O_LAN_SDU_SEND 0x3D -#define I2O_LAN_RECEIVE_POST 0x3E -#define I2O_LAN_RESET 0x35 -#define I2O_LAN_SHUTDOWN 0x37 - -/* Private Class specfic function */ -#define I2O_PRIVATE 0xFF - -/* I2O Executive Function Codes. */ - -#define I2O_EXEC_ADAPTER_ASSIGN 0xB3 -#define I2O_EXEC_ADAPTER_READ 0xB2 -#define I2O_EXEC_ADAPTER_RELEASE 0xB5 -#define I2O_EXEC_BIOS_INFO_SET 0xA5 -#define I2O_EXEC_BOOT_DEVICE_SET 0xA7 -#define I2O_EXEC_CONFIG_VALIDATE 0xBB -#define I2O_EXEC_CONN_SETUP 0xCA -#define I2O_EXEC_DEVICE_ASSIGN 0xB7 -#define I2O_EXEC_DEVICE_RELEASE 0xB9 -#define I2O_EXEC_HRT_GET 0xA8 -#define I2O_EXEC_IOP_CLEAR 0xBE -#define I2O_EXEC_IOP_CONNECT 0xC9 -#define I2O_EXEC_IOP_RESET 0xBD -#define I2O_EXEC_LCT_NOTIFY 0xA2 -#define I2O_EXEC_OUTBOUND_INIT 0xA1 -#define I2O_EXEC_PATH_ENABLE 0xD3 -#define I2O_EXEC_PATH_QUIESCE 0xC5 -#define I2O_EXEC_PATH_RESET 0xD7 -#define I2O_EXEC_STATIC_MF_CREATE 0xDD -#define I2O_EXEC_STATIC_MF_RELEASE 0xDF -#define I2O_EXEC_STATUS_GET 0xA0 -#define I2O_EXEC_SW_DOWNLOAD 0xA9 -#define I2O_EXEC_SW_UPLOAD 0xAB -#define I2O_EXEC_SW_REMOVE 0xAD -#define I2O_EXEC_SYS_ENABLE 0xD1 -#define I2O_EXEC_SYS_MODIFY 0xC1 -#define I2O_EXEC_SYS_QUIESCE 0xC3 -#define I2O_EXEC_SYS_TAB_SET 0xA3 - - /* Init Outbound Q status */ -#define I2O_EXEC_OUTBOUND_INIT_IN_PROGRESS 0x01 -#define I2O_EXEC_OUTBOUND_INIT_REJECTED 0x02 -#define I2O_EXEC_OUTBOUND_INIT_FAILED 0x03 -#define I2O_EXEC_OUTBOUND_INIT_COMPLETE 0x04 - -#define I2O_UTIL_NOP 0x00 - -/* I2O Get Status State values */ - -#define I2O_IOP_STATE_INITIALIZING 0x01 -#define I2O_IOP_STATE_RESET 0x02 -#define I2O_IOP_STATE_HOLD 0x04 -#define I2O_IOP_STATE_READY 0x05 -#define I2O_IOP_STATE_OPERATIONAL 0x08 -#define I2O_IOP_STATE_FAILED 0x10 -#define I2O_IOP_STATE_FAULTED 0x11 - -/* Defines for Request Status Codes: Table 3-1 Reply Status Codes. */ - -#define I2O_REPLY_STATUS_SUCCESS 0x00 -#define I2O_REPLY_STATUS_ABORT_DIRTY 0x01 -#define I2O_REPLY_STATUS_ABORT_NO_DATA_TRANSFER 0x02 -#define I2O_REPLY_STATUS_ABORT_PARTIAL_TRANSFER 0x03 -#define I2O_REPLY_STATUS_ERROR_DIRTY 0x04 -#define I2O_REPLY_STATUS_ERROR_NO_DATA_TRANSFER 0x05 -#define I2O_REPLY_STATUS_ERROR_PARTIAL_TRANSFER 0x06 -#define I2O_REPLY_STATUS_PROCESS_ABORT_DIRTY 0x07 -#define I2O_REPLY_STATUS_PROCESS_ABORT_NO_DATA_TRANSFER 0x08 -#define I2O_REPLY_STATUS_PROCESS_ABORT_PARTIAL_TRANSFER 0x09 -#define I2O_REPLY_STATUS_TRANSACTION_ERROR 0x0A -#define I2O_REPLY_STATUS_PROGRESS_REPORT 0x80 - -/* DetailedStatusCode defines for ALL messages: Table 3-2 Detailed Status Codes.*/ - -#define I2O_DETAIL_STATUS_SUCCESS 0x0000 -#define I2O_DETAIL_STATUS_BAD_KEY 0x0001 -#define I2O_DETAIL_STATUS_CHAIN_BUFFER_TOO_LARGE 0x0002 -#define I2O_DETAIL_STATUS_DEVICE_BUSY 0x0003 -#define I2O_DETAIL_STATUS_DEVICE_LOCKED 0x0004 -#define I2O_DETAIL_STATUS_DEVICE_NOT_AVAILABLE 0x0005 -#define I2O_DETAIL_STATUS_DEVICE_RESET 0x0006 -#define I2O_DETAIL_STATUS_INAPPROPRIATE_FUNCTION 0x0007 -#define I2O_DETAIL_STATUS_INSUFFICIENT_RESOURCE_HARD 0x0008 -#define I2O_DETAIL_STATUS_INSUFFICIENT_RESOURCE_SOFT 0x0009 -#define I2O_DETAIL_STATUS_INVALID_INITIATOR_ADDRESS 0x000A -#define I2O_DETAIL_STATUS_INVALID_MESSAGE_FLAGS 0x000B -#define I2O_DETAIL_STATUS_INVALID_OFFSET 0x000C -#define I2O_DETAIL_STATUS_INVALID_PARAMETER 0x000D -#define I2O_DETAIL_STATUS_INVALID_REQUEST 0x000E -#define I2O_DETAIL_STATUS_INVALID_TARGET_ADDRESS 0x000F -#define I2O_DETAIL_STATUS_MESSAGE_TOO_LARGE 0x0010 -#define I2O_DETAIL_STATUS_MESSAGE_TOO_SMALL 0x0011 -#define I2O_DETAIL_STATUS_MISSING_PARAMETER 0x0012 -#define I2O_DETAIL_STATUS_NO_SUCH_PAGE 0x0013 -#define I2O_DETAIL_STATUS_REPLY_BUFFER_FULL 0x0014 -#define I2O_DETAIL_STATUS_TCL_ERROR 0x0015 -#define I2O_DETAIL_STATUS_TIMEOUT 0x0016 -#define I2O_DETAIL_STATUS_UNKNOWN_ERROR 0x0017 -#define I2O_DETAIL_STATUS_UNKNOWN_FUNCTION 0x0018 -#define I2O_DETAIL_STATUS_UNSUPPORTED_FUNCTION 0x0019 -#define I2O_DETAIL_STATUS_UNSUPPORTED_VERSION 0x001A - - /* I2O msg header defines for VersionOffset */ -#define I2OMSGVER_1_5 0x0001 -#define SGL_OFFSET_0 I2OMSGVER_1_5 -#define SGL_OFFSET_4 (0x0040 | I2OMSGVER_1_5) -#define TRL_OFFSET_5 (0x0050 | I2OMSGVER_1_5) -#define TRL_OFFSET_6 (0x0060 | I2OMSGVER_1_5) - - /* I2O msg header defines for MsgFlags */ -#define MSG_STATIC 0x0100 -#define MSG_64BIT_CNTXT 0x0200 -#define MSG_MULTI_TRANS 0x1000 -#define MSG_FAIL 0x2000 -#define MSG_LAST 0x4000 -#define MSG_REPLY 0x8000 - - /* normal LAN request message MsgFlags and VersionOffset (0x1041) */ -#define LAN_MSG_REQST (MSG_MULTI_TRANS | SGL_OFFSET_4) - - /* minimum size msg */ -#define THREE_WORD_MSG_SIZE 0x00030000 -#define FOUR_WORD_MSG_SIZE 0x00040000 -#define FIVE_WORD_MSG_SIZE 0x00050000 -#define SIX_WORD_MSG_SIZE 0x00060000 -#define SEVEN_WORD_MSG_SIZE 0x00070000 -#define EIGHT_WORD_MSG_SIZE 0x00080000 -#define NINE_WORD_MSG_SIZE 0x00090000 - -/* Special TID Assignments */ - -#define I2O_IOP_TID 0 -#define I2O_HOST_TID 0xB91 - - /* RedCreek I2O private message codes */ -#define RC_PRIVATE_GET_MAC_ADDR 0x0001/**/ /* OBSOLETE */ -#define RC_PRIVATE_SET_MAC_ADDR 0x0002 -#define RC_PRIVATE_GET_NIC_STATS 0x0003 -#define RC_PRIVATE_GET_LINK_STATUS 0x0004 -#define RC_PRIVATE_SET_LINK_SPEED 0x0005 -#define RC_PRIVATE_SET_IP_AND_MASK 0x0006 -/* #define RC_PRIVATE_GET_IP_AND_MASK 0x0007 *//* OBSOLETE */ -#define RC_PRIVATE_GET_LINK_SPEED 0x0008 -#define RC_PRIVATE_GET_FIRMWARE_REV 0x0009 -/* #define RC_PRIVATE_GET_MAC_ADDR 0x000A */ -#define RC_PRIVATE_GET_IP_AND_MASK 0x000B -#define RC_PRIVATE_DEBUG_MSG 0x000C -#define RC_PRIVATE_REPORT_DRIVER_CAPABILITY 0x000D -#define RC_PRIVATE_SET_PROMISCUOUS_MODE 0x000e -#define RC_PRIVATE_GET_PROMISCUOUS_MODE 0x000f -#define RC_PRIVATE_SET_BROADCAST_MODE 0x0010 -#define RC_PRIVATE_GET_BROADCAST_MODE 0x0011 - -#define RC_PRIVATE_REBOOT 0x00FF - -/* I2O message header */ -typedef struct _I2O_MESSAGE_FRAME { - U8 VersionOffset; - U8 MsgFlags; - U16 MessageSize; - BF TargetAddress:I2O_TID_SZ; - BF InitiatorAddress:I2O_TID_SZ; - BF Function:I2O_FUNCTION_SZ; - U32 InitiatorContext; - /* SGL[] */ -} I2O_MESSAGE_FRAME, *PI2O_MESSAGE_FRAME; - - /* assumed a 16K minus 256 byte space for outbound queue message frames */ -#define MSG_FRAME_SIZE 512 -#define NMBR_MSG_FRAMES 30 - - /* - ** in reserved space right after PAB in host memory is area for returning - ** values from card - */ - -/* -** typedef NICSTAT -** -** Data structure for NIC statistics retruned from PCI card. Data copied from -** here to user allocated RCLINKSTATS (see rclanmtl.h) structure. -*/ -typedef struct tag_NicStat { - unsigned long TX_good; - unsigned long TX_maxcol; - unsigned long TX_latecol; - unsigned long TX_urun; - unsigned long TX_crs; /* lost carrier sense */ - unsigned long TX_def; /* transmit deferred */ - unsigned long TX_singlecol; /* single collisions */ - unsigned long TX_multcol; - unsigned long TX_totcol; - unsigned long Rcv_good; - unsigned long Rcv_CRCerr; - unsigned long Rcv_alignerr; - unsigned long Rcv_reserr; /* rnr'd pkts */ - unsigned long Rcv_orun; - unsigned long Rcv_cdt; - unsigned long Rcv_runt; - unsigned long dump_status; /* last field directly from the chip */ -} NICSTAT, *P_NICSTAT; - -#define DUMP_DONE 0x0000A005 /* completed statistical dump */ -#define DUMP_CLEAR 0x0000A007 /* completed stat dump and clear counters */ - -static volatile int msgFlag; - -/* local function prototypes */ -static void ProcessOutboundI2OMsg (PPAB pPab, U32 phyMsgAddr); -static int FillI2OMsgSGLFromTCB (PU32 pMsg, PRCTCB pXmitCntrlBlock); -static int GetI2OStatus (PPAB pPab); -static int SendI2OOutboundQInitMsg (PPAB pPab); -static int SendEnableSysMsg (PPAB pPab); - -/* -** ========================================================================= -** RCInitI2OMsgLayer() -** -** Initialize the RedCreek I2O Module and adapter. -** -** Inputs: dev - the devices net_device struct -** TransmitCallbackFunction - address of transmit callback function -** ReceiveCallbackFunction - address of receive callback function -** -** private message block is allocated by user. It must be in locked pages. -** p_msgbuf and p_phymsgbuf point to the same location. Must be contigous -** memory block of a minimum of 16K byte and long word aligned. -** ========================================================================= -*/ -RC_RETURN -RCInitI2OMsgLayer (struct net_device *dev, - PFNTXCALLBACK TransmitCallbackFunction, - PFNRXCALLBACK ReceiveCallbackFunction, - PFNCALLBACK RebootCallbackFunction) -{ - int result; - PPAB pPab; - U32 pciBaseAddr = dev->base_addr; - PDPA pDpa = dev->priv; - PU8 p_msgbuf = pDpa->msgbuf; - PU8 p_phymsgbuf = (PU8) pDpa->msgbuf_dma; - - dprintk - ("InitI2O: Adapter:0x%04ux ATU:0x%08ulx msgbuf:%p phymsgbuf:0x%08ulx\n" - "TransmitCallbackFunction:0x%08ulx ReceiveCallbackFunction:0x%08ulx\n", - pDpa->id, pciBaseAddr, p_msgbuf, (u32) p_phymsgbuf, - (u32) TransmitCallbackFunction, (u32) ReceiveCallbackFunction); - - /* Check if this interface already initialized - if so, shut it down */ - if (pDpa->pPab != NULL) { - printk (KERN_WARNING - "(rcpci45 driver:) pDpa->pPab [%d] != NULL\n", - pDpa->id); -/* RCResetLANCard(pDpa->id, 0, (PU32)NULL, (PFNCALLBACK)NULL); */ - pDpa->pPab = NULL; - } - - /* store adapter instance values in adapter block. - * Adapter block is at beginning of message buffer */ - - pPab = kmalloc (sizeof (*pPab), GFP_KERNEL); - if (!pPab) { - printk (KERN_ERR - "(rcpci45 driver:) RCInitI2OMsgLayer: Could not allocate memory for PAB struct!\n"); - result = RC_RTN_MALLOC_ERROR; - goto err_out; - } - - memset (pPab, 0, sizeof (*pPab)); - pDpa->pPab = pPab; - pPab->p_atu = (PATU) pciBaseAddr; - pPab->pPci45LinBaseAddr = (PU8) pciBaseAddr; - - /* Set outbound message frame addr */ - pPab->outMsgBlockPhyAddr = (U32) p_phymsgbuf; - pPab->pLinOutMsgBlock = (PU8) p_msgbuf; - - /* store callback function addresses */ - pPab->pTransCallbackFunc = TransmitCallbackFunction; - pPab->pRecvCallbackFunc = ReceiveCallbackFunction; - pPab->pRebootCallbackFunc = RebootCallbackFunction; - pPab->pCallbackFunc = (PFNCALLBACK) NULL; - - /* - ** Initialize I2O IOP - */ - result = GetI2OStatus (pPab); - - if (result != RC_RTN_NO_ERROR) - goto err_out_dealloc; - - if (pPab->IOPState == I2O_IOP_STATE_OPERATIONAL) { - printk (KERN_INFO - "(rcpci45 driver:) pPab->IOPState == op: resetting adapter\n"); - RCResetLANCard (dev, 0, (PU32) NULL, (PFNCALLBACK) NULL); - } - - result = SendI2OOutboundQInitMsg (pPab); - - if (result != RC_RTN_NO_ERROR) - goto err_out_dealloc; - - result = SendEnableSysMsg (pPab); - - if (result != RC_RTN_NO_ERROR) - goto err_out_dealloc; - - return RC_RTN_NO_ERROR; - - err_out_dealloc: - kfree (pPab); - err_out: - return result; -} - -/* -** ========================================================================= -** Disable and Enable I2O interrupts. I2O interrupts are enabled at Init time -** but can be disabled and re-enabled through these two function calls. -** Packets will still be put into any posted received buffers and packets will -** be sent through RCI2OSendPacket() functions. Disabling I2O interrupts -** will prevent hardware interrupt to host even though the outbound I2O msg -** queue is not emtpy. -** ========================================================================= -*/ -#define i960_OUT_POST_Q_INT_BIT 0x0008 /* bit set masks interrupts */ - -RC_RETURN -RCDisableI2OInterrupts (struct net_device * dev) -{ - PPAB pPab = ((PDPA) dev->priv)->pPab; - - if (pPab == NULL) - return RC_RTN_ADPTR_NOT_REGISTERED; - - pPab->p_atu->OutIntMask |= i960_OUT_POST_Q_INT_BIT; - - return RC_RTN_NO_ERROR; -} - -RC_RETURN -RCEnableI2OInterrupts (struct net_device * dev) -{ - PPAB pPab = ((PDPA) dev->priv)->pPab; - - if (pPab == NULL) - return RC_RTN_ADPTR_NOT_REGISTERED; - - pPab->p_atu->OutIntMask &= ~i960_OUT_POST_Q_INT_BIT; - - return RC_RTN_NO_ERROR; - -} - -/* -** ========================================================================= -** RCI2OSendPacket() -** ========================================================================= -*/ -RC_RETURN -RCI2OSendPacket (struct net_device * dev, U32 InitiatorContext, - PRCTCB pTransCtrlBlock) -{ - U32 msgOffset; - PU32 pMsg; - int size; - PPAB pPab = ((PDPA) dev->priv)->pPab; - - dprintk ("RCI2OSendPacket()...\n"); - - if (pPab == NULL) - return RC_RTN_ADPTR_NOT_REGISTERED; - - /* get Inbound free Q entry - reading from In Q gets free Q entry */ - /* offset to Msg Frame in PCI msg block */ - - msgOffset = pPab->p_atu->InQueue; - - if (msgOffset == 0xFFFFFFFF) { - dprintk ("RCI2OSendPacket(): Inbound Free Q empty!\n"); - return RC_RTN_FREE_Q_EMPTY; - } - - /* calc virtual address of msg - virtual already mapped to physical */ - pMsg = (PU32) (pPab->pPci45LinBaseAddr + msgOffset); - - size = FillI2OMsgSGLFromTCB (pMsg + 4, pTransCtrlBlock); - - if (size == -1) { /* error processing TCB - send NOP msg */ - dprintk ("RCI2OSendPacket(): Error Rrocess TCB!\n"); - pMsg[0] = THREE_WORD_MSG_SIZE | SGL_OFFSET_0; - pMsg[1] = - I2O_UTIL_NOP << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID; - return RC_RTN_TCB_ERROR; - } else { /* send over msg header */ - - pMsg[0] = (size + 4) << 16 | LAN_MSG_REQST; /* send over message size and flags */ - pMsg[1] = - I2O_LAN_PACKET_SEND << 24 | I2O_HOST_TID << 12 | - RC_LAN_TARGET_ID; - pMsg[2] = InitiatorContext; - pMsg[3] = 0; /* batch reply */ - /* post to Inbound Post Q */ - pPab->p_atu->InQueue = msgOffset; - return RC_RTN_NO_ERROR; - } -} - -/* -** ========================================================================= -** RCI2OPostRecvBuffer() -** -** inputs: pBufrCntrlBlock - pointer to buffer control block -** -** returns TRUE if successful in sending message, else FALSE. -** ========================================================================= -*/ -RC_RETURN -RCPostRecvBuffers (struct net_device * dev, PRCTCB pTransCtrlBlock) -{ - U32 msgOffset; - PU32 pMsg; - int size; - PPAB pPab = ((PDPA) dev->priv)->pPab; - - dprintk ("RCPostRecvBuffers()...\n"); - - /* search for DeviceHandle */ - - if (pPab == NULL) - return RC_RTN_ADPTR_NOT_REGISTERED; - - /* get Inbound free Q entry - reading from In Q gets free Q entry */ - /* offset to Msg Frame in PCI msg block */ - msgOffset = pPab->p_atu->InQueue; - - if (msgOffset == 0xFFFFFFFF) { - dprintk ("RCPostRecvBuffers(): Inbound Free Q empty!\n"); - return RC_RTN_FREE_Q_EMPTY; - } - /* calc virtual address of msg - virtual already mapped to physical */ - pMsg = (PU32) (pPab->pPci45LinBaseAddr + msgOffset); - - size = FillI2OMsgSGLFromTCB (pMsg + 4, pTransCtrlBlock); - - if (size == -1) { /* error prcessing TCB - send 3 DWORD private msg == NOP */ - dprintk - ("RCPostRecvBuffers(): Error Processing TCB! size = %d\n", - size); - pMsg[0] = THREE_WORD_MSG_SIZE | SGL_OFFSET_0; - pMsg[1] = - I2O_UTIL_NOP << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID; - /* post to Post Q */ - pPab->p_atu->InQueue = msgOffset; - return RC_RTN_TCB_ERROR; - } else { /* send over size msg header */ - - pMsg[0] = (size + 4) << 16 | LAN_MSG_REQST; /* send over message size and flags */ - pMsg[1] = - I2O_LAN_RECEIVE_POST << 24 | I2O_HOST_TID << 12 | - RC_LAN_TARGET_ID; - pMsg[2] = DEFAULT_RECV_INIT_CONTEXT; - pMsg[3] = *(PU32) pTransCtrlBlock; /* number of packet buffers */ - /* post to Post Q */ - pPab->p_atu->InQueue = msgOffset; - return RC_RTN_NO_ERROR; - } -} - -/* -** ========================================================================= -** RCProcI2OMsgQ() -** -** Process I2O outbound message queue until empty. -** ========================================================================= -*/ -irqreturn_t -RCProcI2OMsgQ (struct net_device *dev) -{ - U32 phyAddrMsg; - PU8 p8Msg; - PU32 p32; - U16 count; - PPAB pPab = ((PDPA) dev->priv)->pPab; - unsigned char debug_msg[20]; - - if (pPab == NULL) - return IRQ_NONE; - - phyAddrMsg = pPab->p_atu->OutQueue; - - while (phyAddrMsg != 0xFFFFFFFF) { - p8Msg = - pPab->pLinOutMsgBlock + (phyAddrMsg - - pPab->outMsgBlockPhyAddr); - p32 = (PU32) p8Msg; - - dprintk ("msg: 0x%x 0x%x \n", p8Msg[7], p32[5]); - - /* Send Packet Reply Msg */ - if (I2O_LAN_PACKET_SEND == p8Msg[7]) { /* function code byte */ - count = *(PU16) (p8Msg + 2); - count -= p8Msg[0] >> 4; - /* status, count, context[], adapter */ - (*pPab->pTransCallbackFunc) (p8Msg[19], count, p32 + 5, - dev); - } else if (I2O_LAN_RECEIVE_POST == p8Msg[7]) { /* Receive Packet Reply Msg */ - dprintk - ("I2O_RECV_REPLY pPab:0x%08ulx p8Msg:0x%08ulx p32:0x%08ulx\n", - (u32) pPab, (u32) p8Msg, (u32) p32); - dprintk ("msg: 0x%08ulx:0x%08ulx:0x%08ulx:0x%08ulx\n", - p32[0], p32[1], p32[2], p32[3]); - dprintk (" 0x%08ulx:0x%08ulx:0x%08ulx:0x%08ulx\n", - p32[4], p32[5], p32[6], p32[7]); - dprintk (" 0x%08ulx:0X%08ulx:0x%08ulx:0x%08ulx\n", - p32[8], p32[9], p32[10], p32[11]); - /* status, count, buckets remaining, packetParmBlock, adapter */ - (*pPab->pRecvCallbackFunc) (p8Msg[19], p8Msg[12], - p32[5], p32 + 6, dev); - } else if (I2O_LAN_RESET == p8Msg[7] - || I2O_LAN_SHUTDOWN == p8Msg[7]) - if (pPab->pCallbackFunc) - (*pPab->pCallbackFunc) (p8Msg[19], 0, 0, dev); - else - pPab->pCallbackFunc = (PFNCALLBACK) 1; - else if (I2O_PRIVATE == p8Msg[7]) { - dprintk ("i2o private 0x%x, 0x%x \n", p8Msg[7], p32[5]); - switch (p32[5]) { - case RC_PRIVATE_DEBUG_MSG: - msgFlag = 1; - dprintk ("Received I2O_PRIVATE msg\n"); - debug_msg[15] = (p32[6] & 0xff000000) >> 24; - debug_msg[14] = (p32[6] & 0x00ff0000) >> 16; - debug_msg[13] = (p32[6] & 0x0000ff00) >> 8; - debug_msg[12] = (p32[6] & 0x000000ff); - - debug_msg[11] = (p32[7] & 0xff000000) >> 24; - debug_msg[10] = (p32[7] & 0x00ff0000) >> 16; - debug_msg[9] = (p32[7] & 0x0000ff00) >> 8; - debug_msg[8] = (p32[7] & 0x000000ff); - - debug_msg[7] = (p32[8] & 0xff000000) >> 24; - debug_msg[6] = (p32[8] & 0x00ff0000) >> 16; - debug_msg[5] = (p32[8] & 0x0000ff00) >> 8; - debug_msg[4] = (p32[8] & 0x000000ff); - - debug_msg[3] = (p32[9] & 0xff000000) >> 24; - debug_msg[2] = (p32[9] & 0x00ff0000) >> 16; - debug_msg[1] = (p32[9] & 0x0000ff00) >> 8; - debug_msg[0] = (p32[9] & 0x000000ff); - - debug_msg[16] = '\0'; - dprintk ("%s", debug_msg); - break; - case RC_PRIVATE_REBOOT: - dprintk ("Adapter reboot initiated...\n"); - if (pPab->pRebootCallbackFunc) - (*pPab->pRebootCallbackFunc) (0, 0, 0, - dev); - break; - default: - printk (KERN_WARNING - "(rcpci45 driver:) Unknown private I2O msg received: 0x%x\n", - p32[5]); - break; - } - } - - /* - ** Process other Msg's - */ - else - ProcessOutboundI2OMsg (pPab, phyAddrMsg); - - /* return MFA to outbound free Q */ - pPab->p_atu->OutQueue = phyAddrMsg; - - /* any more msgs? */ - phyAddrMsg = pPab->p_atu->OutQueue; - } - - return IRQ_HANDLED; -} - -/* -** ========================================================================= -** Returns LAN interface statistical counters to space provided by caller at -** StatsReturnAddr. Returns 0 if success, else RC_RETURN code. -** This function will call the WaitCallback function provided by -** user while waiting for card to respond. -** ========================================================================= -*/ -RC_RETURN -RCGetLinkStatistics (struct net_device *dev, - P_RCLINKSTATS StatsReturnAddr, - PFNWAITCALLBACK WaitCallback) -{ - U32 msgOffset; - volatile U32 timeout; - volatile PU32 pMsg; - volatile PU32 p32, pReturnAddr; - P_NICSTAT pStats; - int i; - PPAB pPab = ((PDPA) dev->priv)->pPab; - -/*dprintk("Get82558Stats() StatsReturnAddr:0x%08ulx\n", StatsReturnAddr); */ - - if (pPab == NULL) - return RC_RTN_ADPTR_NOT_REGISTERED; - - msgOffset = pPab->p_atu->InQueue; - - if (msgOffset == 0xFFFFFFFF) { - dprintk ("Get8255XStats(): Inbound Free Q empty!\n"); - return RC_RTN_FREE_Q_EMPTY; - } - - /* calc virtual address of msg - virtual already mapped to physical */ - pMsg = (PU32) (pPab->pPci45LinBaseAddr + msgOffset); - -/*dprintk("Get82558Stats - pMsg = 0x%08ulx, InQ msgOffset = 0x%08ulx\n", pMsg, msgOffset);*/ -/*dprintk("Get82558Stats - pMsg = 0x%08X, InQ msgOffset = 0x%08X\n", pMsg, msgOffset);*/ - - pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0; - pMsg[1] = I2O_PRIVATE << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID; - pMsg[2] = DEFAULT_RECV_INIT_CONTEXT; - pMsg[3] = 0x112; /* transaction context */ - pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_GET_NIC_STATS; - pMsg[5] = pPab->outMsgBlockPhyAddr; - - p32 = (PU32) pPab->outMsgBlockPhyAddr; - pStats = (P_NICSTAT) pPab->pLinOutMsgBlock; - pStats->dump_status = 0xFFFFFFFF; - - /* post to Inbound Post Q */ - pPab->p_atu->InQueue = msgOffset; - - timeout = 100000; - while (1) { - if (WaitCallback) - (*WaitCallback) (); - - udelay (10); - - if (pStats->dump_status != 0xFFFFFFFF) - break; - - if (!timeout--) { - dprintk - ("RCGet82558Stats() Timeout waiting for NIC statistics\n"); - return RC_RTN_MSG_REPLY_TIMEOUT; - } - } - - pReturnAddr = (PU32) StatsReturnAddr; - - /* copy Nic stats to user's structure */ - for (i = 0; i < (int) sizeof (RCLINKSTATS) / 4; i++) - pReturnAddr[i] = p32[i]; - - return RC_RTN_NO_ERROR; -} - -/* -** ========================================================================= -** Get82558LinkStatus() -** ========================================================================= -*/ -RC_RETURN -RCGetLinkStatus (struct net_device * dev, PU32 ReturnAddr, - PFNWAITCALLBACK WaitCallback) -{ - U32 msgOffset; - volatile U32 timeout; - volatile PU32 pMsg; - volatile PU32 p32; - PPAB pPab = ((PDPA) dev->priv)->pPab; - - dprintk ("Get82558LinkStatus() ReturnPhysAddr:0x%08ulx\n", - (u32) ReturnAddr); - - if (pPab == NULL) - return RC_RTN_ADPTR_NOT_REGISTERED; - - msgOffset = pPab->p_atu->InQueue; - - if (msgOffset == 0xFFFFFFFF) { - dprintk ("Get82558LinkStatus(): Inbound Free Q empty!\n"); - return RC_RTN_FREE_Q_EMPTY; - } - - /* calc virtual address of msg - virtual already mapped to physical */ - pMsg = (PU32) (pPab->pPci45LinBaseAddr + msgOffset); -/*dprintk("Get82558LinkStatus - pMsg = 0x%08ulx, InQ msgOffset = 0x%08ulx\n", pMsg, msgOffset);*/ -/*dprintk("Get82558LinkStatus - pMsg = 0x%08X, InQ msgOffset = 0x%08X\n", pMsg, msgOffset);*/ - - pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0; - pMsg[1] = I2O_PRIVATE << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID; - pMsg[2] = DEFAULT_RECV_INIT_CONTEXT; - pMsg[3] = 0x112; /* transaction context */ - pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_GET_LINK_STATUS; - pMsg[5] = pPab->outMsgBlockPhyAddr; - - p32 = (PU32) pPab->pLinOutMsgBlock; - *p32 = 0xFFFFFFFF; - - /* post to Inbound Post Q */ - pPab->p_atu->InQueue = msgOffset; - - timeout = 100000; - while (1) { - if (WaitCallback) - (*WaitCallback) (); - - udelay (10); - - if (*p32 != 0xFFFFFFFF) - break; - - if (!timeout--) { - dprintk ("Timeout waiting for link status\n"); - return RC_RTN_MSG_REPLY_TIMEOUT; - } - } - - *ReturnAddr = *p32; /* 1 = up 0 = down */ - - return RC_RTN_NO_ERROR; - -} - -/* -** ========================================================================= -** RCGetMAC() -** -** get the MAC address the adapter is listening for in non-promiscous mode. -** MAC address is in media format. -** ========================================================================= -*/ -RC_RETURN -RCGetMAC (struct net_device * dev, PFNWAITCALLBACK WaitCallback) -{ - unsigned timeout; - U32 off; - PU8 mac = dev->dev_addr; - PU32 p; - U32 temp[2]; - PPAB pPab = ((PDPA) dev->priv)->pPab; - PATU p_atu; - - if (pPab == NULL) - return RC_RTN_ADPTR_NOT_REGISTERED; - - p_atu = pPab->p_atu; - - p_atu->EtherMacLow = 0; /* first zero return data */ - p_atu->EtherMacHi = 0; - - off = p_atu->InQueue; /* get addresss of message */ - - if (0xFFFFFFFF == off) - return RC_RTN_FREE_Q_EMPTY; - - p = (PU32) (pPab->pPci45LinBaseAddr + off); - - dprintk ("RCGetMAC: p_atu 0x%08x, off 0x%08x, p 0x%08x\n", - (uint) p_atu, (uint) off, (uint) p); - /* setup private message */ - p[0] = FIVE_WORD_MSG_SIZE | SGL_OFFSET_0; - p[1] = I2O_PRIVATE << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID; - p[2] = 0; /* initiator context */ - p[3] = 0x218; /* transaction context */ - p[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_GET_MAC_ADDR; - - p_atu->InQueue = off; /* send it to the I2O device */ - dprintk ("RCGetMAC: p_atu 0x%08x, off 0x%08x, p 0x%08x\n", - (uint) p_atu, (uint) off, (uint) p); - - /* wait for the rcpci45 board to update the info */ - timeout = 1000000; - while (0 == p_atu->EtherMacLow) { - if (WaitCallback) - (*WaitCallback) (); - - udelay (10); - - if (!timeout--) { - printk ("rc_getmac: Timeout\n"); - return RC_RTN_MSG_REPLY_TIMEOUT; - } - } - - /* read the mac address */ - temp[0] = p_atu->EtherMacLow; - temp[1] = p_atu->EtherMacHi; - memcpy ((char *) mac, (char *) temp, 6); - - dprintk ("rc_getmac: 0x%x\n", (u32) mac); - - return RC_RTN_NO_ERROR; -} - -/* -** ========================================================================= -** RCSetMAC() -** -** set MAC address the adapter is listening for in non-promiscous mode. -** MAC address is in media format. -** ========================================================================= -*/ -RC_RETURN -RCSetMAC (struct net_device * dev, PU8 mac) -{ - U32 off; - PU32 pMsg; - PPAB pPab = ((PDPA) dev->priv)->pPab; - - if (pPab == NULL) - return RC_RTN_ADPTR_NOT_REGISTERED; - - off = pPab->p_atu->InQueue; /* get addresss of message */ - - if (0xFFFFFFFF == off) - return RC_RTN_FREE_Q_EMPTY; - - pMsg = (PU32) (pPab->pPci45LinBaseAddr + off); - - /* setup private message */ - pMsg[0] = SEVEN_WORD_MSG_SIZE | SGL_OFFSET_0; - pMsg[1] = I2O_PRIVATE << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID; - pMsg[2] = 0; /* initiator context */ - pMsg[3] = 0x219; /* transaction context */ - pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_SET_MAC_ADDR; - pMsg[5] = *(unsigned *) mac; /* first four bytes */ - pMsg[6] = *(unsigned *) (mac + 4); /* last two bytes */ - - pPab->p_atu->InQueue = off; /* send it to the I2O device */ - - return RC_RTN_NO_ERROR; -} - -/* -** ========================================================================= -** RCSetLinkSpeed() -** -** set ethernet link speed. -** input: speedControl - determines action to take as follows -** 0 = reset and auto-negotiate (NWay) -** 1 = Full Duplex 100BaseT -** 2 = Half duplex 100BaseT -** 3 = Full Duplex 10BaseT -** 4 = Half duplex 10BaseT -** all other values are ignore (do nothing) -** ========================================================================= -*/ -RC_RETURN -RCSetLinkSpeed (struct net_device * dev, U16 LinkSpeedCode) -{ - U32 off; - PU32 pMsg; - PPAB pPab = ((PDPA) dev->priv)->pPab; - - if (pPab == NULL) - return RC_RTN_ADPTR_NOT_REGISTERED; - - off = pPab->p_atu->InQueue; /* get addresss of message */ - - if (0xFFFFFFFF == off) - return RC_RTN_FREE_Q_EMPTY; - - pMsg = (PU32) (pPab->pPci45LinBaseAddr + off); - - /* setup private message */ - pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0; - pMsg[1] = I2O_PRIVATE << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID; - pMsg[2] = 0; /* initiator context */ - pMsg[3] = 0x219; /* transaction context */ - pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_SET_LINK_SPEED; - pMsg[5] = LinkSpeedCode; /* link speed code */ - - pPab->p_atu->InQueue = off; /* send it to the I2O device */ - - return RC_RTN_NO_ERROR; -} - -/* -** ========================================================================= -** RCSetPromiscuousMode() -** -** Defined values for Mode: -** 0 - turn off promiscuous mode -** 1 - turn on promiscuous mode -** -** ========================================================================= -*/ -RC_RETURN -RCSetPromiscuousMode (struct net_device * dev, U16 Mode) -{ - U32 off; - PU32 pMsg; - PPAB pPab = ((PDPA) dev->priv)->pPab; - - if (pPab == NULL) - return RC_RTN_ADPTR_NOT_REGISTERED; - - off = pPab->p_atu->InQueue; /* get addresss of message */ - - if (0xFFFFFFFF == off) - return RC_RTN_FREE_Q_EMPTY; - - pMsg = (PU32) (pPab->pPci45LinBaseAddr + off); - - /* setup private message */ - pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0; - pMsg[1] = I2O_PRIVATE << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID; - pMsg[2] = 0; /* initiator context */ - pMsg[3] = 0x219; /* transaction context */ - pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_SET_PROMISCUOUS_MODE; - pMsg[5] = Mode; /* promiscuous mode setting */ - - pPab->p_atu->InQueue = off; /* send it to the device */ - - return RC_RTN_NO_ERROR; -} - -/* -** ========================================================================= -** RCGetPromiscuousMode() -** -** get promiscuous mode setting -** -** Possible return values placed in pMode: -** 0 = promisuous mode not set -** 1 = promisuous mode is set -** -** ========================================================================= -*/ -RC_RETURN -RCGetPromiscuousMode (struct net_device * dev, PU32 pMode, - PFNWAITCALLBACK WaitCallback) -{ - U32 msgOffset, timeout; - PU32 pMsg; - volatile PU32 p32; - PPAB pPab = ((PDPA) dev->priv)->pPab; - - msgOffset = pPab->p_atu->InQueue; - - if (msgOffset == 0xFFFFFFFF) { - printk (KERN_WARNING - "(rcpci45 driver:) RCGetLinkSpeed(): Inbound Free Q empty!\n"); - return RC_RTN_FREE_Q_EMPTY; - } - - /* calc virtual address of msg - virtual already mapped to physical */ - pMsg = (PU32) (pPab->pPci45LinBaseAddr + msgOffset); - - /* virtual pointer to return buffer - clear first two dwords */ - p32 = (volatile PU32) pPab->pLinOutMsgBlock; - p32[0] = 0xff; - - /* setup private message */ - pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0; - pMsg[1] = I2O_PRIVATE << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID; - pMsg[2] = 0; /* initiator context */ - pMsg[3] = 0x219; /* transaction context */ - pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_GET_PROMISCUOUS_MODE; - /* phys address to return status - area right after PAB */ - pMsg[5] = pPab->outMsgBlockPhyAddr; - - /* post to Inbound Post Q */ - - pPab->p_atu->InQueue = msgOffset; - - /* wait for response */ - timeout = 1000000; - while (1) { - if (WaitCallback) - (*WaitCallback) (); - - udelay (10); /* please don't hog the bus!!! */ - - if (p32[0] != 0xff) - break; - - if (!timeout--) { - dprintk - ("Timeout waiting for promiscuous mode from adapter\n"); - dprintk ("0x%8x\n", p32[0]); - return RC_RTN_NO_LINK_SPEED; - } - } - - /* get mode */ - *pMode = (U8) ((volatile PU8) p32)[0] & 0x0f; - - return RC_RTN_NO_ERROR; -} - -/* -** ========================================================================= -** RCSetBroadcastMode() -** -** Defined values for Mode: -** 0 - turn off promiscuous mode -** 1 - turn on promiscuous mode -** -** ========================================================================= -*/ -RC_RETURN -RCSetBroadcastMode (struct net_device * dev, U16 Mode) -{ - U32 off; - PU32 pMsg; - PPAB pPab = ((PDPA) dev->priv)->pPab; - - if (pPab == NULL) - return RC_RTN_ADPTR_NOT_REGISTERED; - - off = pPab->p_atu->InQueue; /* get addresss of message */ - - if (0xFFFFFFFF == off) - return RC_RTN_FREE_Q_EMPTY; - - pMsg = (PU32) (pPab->pPci45LinBaseAddr + off); - - /* setup private message */ - pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0; - pMsg[1] = I2O_PRIVATE << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID; - pMsg[2] = 0; /* initiator context */ - pMsg[3] = 0x219; /* transaction context */ - pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_SET_BROADCAST_MODE; - pMsg[5] = Mode; /* promiscuous mode setting */ - - pPab->p_atu->InQueue = off; /* send it to the device */ - - return RC_RTN_NO_ERROR; -} - -/* -** ========================================================================= -** RCGetBroadcastMode() -** -** get promiscuous mode setting -** -** Possible return values placed in pMode: -** 0 = promisuous mode not set -** 1 = promisuous mode is set -** -** ========================================================================= -*/ -RC_RETURN -RCGetBroadcastMode (struct net_device * dev, PU32 pMode, - PFNWAITCALLBACK WaitCallback) -{ - U32 msgOffset, timeout; - PU32 pMsg; - volatile PU32 p32; - PPAB pPab = ((PDPA) dev->priv)->pPab; - - msgOffset = pPab->p_atu->InQueue; - - if (msgOffset == 0xFFFFFFFF) { - printk (KERN_WARNING - "(rcpci45 driver:) RCGetLinkSpeed(): Inbound Free Q empty!\n"); - return RC_RTN_FREE_Q_EMPTY; - } - - /* calc virtual address of msg - virtual already mapped to physical */ - pMsg = (PU32) (pPab->pPci45LinBaseAddr + msgOffset); - - /* virtual pointer to return buffer - clear first two dwords */ - p32 = (volatile PU32) pPab->pLinOutMsgBlock; - p32[0] = 0xff; - - /* setup private message */ - pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0; - pMsg[1] = I2O_PRIVATE << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID; - pMsg[2] = 0; /* initiator context */ - pMsg[3] = 0x219; /* transaction context */ - pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_GET_BROADCAST_MODE; - /* phys address to return status - area right after PAB */ - pMsg[5] = pPab->outMsgBlockPhyAddr; - - /* post to Inbound Post Q */ - - pPab->p_atu->InQueue = msgOffset; - - /* wait for response */ - timeout = 1000000; - while (1) { - if (WaitCallback) - (*WaitCallback) (); - - udelay (10); /* please don't hog the bus!!! */ - - if (p32[0] != 0xff) - break; - - if (!timeout--) { - printk (KERN_WARNING - "(rcpci45 driver:) Timeout waiting for promiscuous mode from adapter\n"); - printk (KERN_WARNING "(rcpci45 driver:) 0x%8x\n", - p32[0]); - return RC_RTN_NO_LINK_SPEED; - } - } - - /* get mode */ - *pMode = (U8) ((volatile PU8) p32)[0] & 0x0f; - - return RC_RTN_NO_ERROR; -} - -/* -** ========================================================================= -** RCGetLinkSpeed() -** -** get ethernet link speed. -** -** 0 = Unknown -** 1 = Full Duplex 100BaseT -** 2 = Half duplex 100BaseT -** 3 = Full Duplex 10BaseT -** 4 = Half duplex 10BaseT -** -** ========================================================================= -*/ -RC_RETURN -RCGetLinkSpeed (struct net_device * dev, PU32 pLinkSpeedCode, - PFNWAITCALLBACK WaitCallback) -{ - U32 msgOffset, timeout; - PU32 pMsg; - volatile PU32 p32; - U8 IOPLinkSpeed; - PPAB pPab = ((PDPA) dev->priv)->pPab; - - msgOffset = pPab->p_atu->InQueue; - - if (msgOffset == 0xFFFFFFFF) { - printk (KERN_WARNING - "(rcpci45 driver:) RCGetLinkSpeed(): Inbound Free Q empty!\n"); - return RC_RTN_FREE_Q_EMPTY; - } - - /* calc virtual address of msg - virtual already mapped to physical */ - pMsg = (PU32) (pPab->pPci45LinBaseAddr + msgOffset); - - /* virtual pointer to return buffer - clear first two dwords */ - p32 = (volatile PU32) pPab->pLinOutMsgBlock; - p32[0] = 0xff; - - /* setup private message */ - pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0; - pMsg[1] = I2O_PRIVATE << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID; - pMsg[2] = 0; /* initiator context */ - pMsg[3] = 0x219; /* transaction context */ - pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_GET_LINK_SPEED; - /* phys address to return status - area right after PAB */ - pMsg[5] = pPab->outMsgBlockPhyAddr; - - /* post to Inbound Post Q */ - - pPab->p_atu->InQueue = msgOffset; - - /* wait for response */ - timeout = 1000000; - while (1) { - if (WaitCallback) - (*WaitCallback) (); - - udelay (10); /* please don't hog the bus!!! */ - - if (p32[0] != 0xff) - break; - - if (!timeout--) { - dprintk ("Timeout waiting for link speed from IOP\n"); - dprintk ("0x%8x\n", p32[0]); - return RC_RTN_NO_LINK_SPEED; - } - } - - /* get Link speed */ - IOPLinkSpeed = (U8) ((volatile PU8) p32)[0] & 0x0f; - - *pLinkSpeedCode = IOPLinkSpeed; - - return RC_RTN_NO_ERROR; -} - -/* -** ========================================================================= -** RCReportDriverCapability(struct net_device *dev, U32 capability) -** -** Currently defined bits: -** WARM_REBOOT_CAPABLE 0x01 -** -** ========================================================================= -*/ -RC_RETURN -RCReportDriverCapability (struct net_device * dev, U32 capability) -{ - U32 off; - PU32 pMsg; - PPAB pPab = ((PDPA) dev->priv)->pPab; - - if (pPab == NULL) - return RC_RTN_ADPTR_NOT_REGISTERED; - - off = pPab->p_atu->InQueue; /* get addresss of message */ - - if (0xFFFFFFFF == off) - return RC_RTN_FREE_Q_EMPTY; - - pMsg = (PU32) (pPab->pPci45LinBaseAddr + off); - - /* setup private message */ - pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0; - pMsg[1] = I2O_PRIVATE << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID; - pMsg[2] = 0; /* initiator context */ - pMsg[3] = 0x219; /* transaction context */ - pMsg[4] = - RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_REPORT_DRIVER_CAPABILITY; - pMsg[5] = capability; - - pPab->p_atu->InQueue = off; /* send it to the I2O device */ - - return RC_RTN_NO_ERROR; -} - -/* -** ========================================================================= -** RCGetFirmwareVer() -** -** Return firmware version in the form "SoftwareVersion : Bt BootVersion" -** -** ========================================================================= -*/ -RC_RETURN -RCGetFirmwareVer (struct net_device * dev, PU8 pFirmString, - PFNWAITCALLBACK WaitCallback) -{ - U32 msgOffset, timeout; - PU32 pMsg; - volatile PU32 p32; - PPAB pPab = ((PDPA) dev->priv)->pPab; - - msgOffset = pPab->p_atu->InQueue; - if (msgOffset == 0xFFFFFFFF) { - dprintk ("RCGetFirmwareVer(): Inbound Free Q empty!\n"); - return RC_RTN_FREE_Q_EMPTY; - } - - /* calc virtual address of msg - virtual already mapped to physical */ - pMsg = (PU32) (pPab->pPci45LinBaseAddr + msgOffset); - - /* virtual pointer to return buffer - clear first two dwords */ - p32 = (volatile PU32) pPab->pLinOutMsgBlock; - p32[0] = 0xff; - - /* setup private message */ - pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0; - pMsg[1] = I2O_PRIVATE << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID; - pMsg[2] = 0; /* initiator context */ - pMsg[3] = 0x219; /* transaction context */ - pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_GET_FIRMWARE_REV; - /* phys address to return status - area right after PAB */ - pMsg[5] = pPab->outMsgBlockPhyAddr; - - /* post to Inbound Post Q */ - - pPab->p_atu->InQueue = msgOffset; - - /* wait for response */ - timeout = 1000000; - while (1) { - if (WaitCallback) - (*WaitCallback) (); - - udelay (10); /* please don't hog the bus!!! */ - - if (p32[0] != 0xff) - break; - - if (!timeout--) { - dprintk ("Timeout waiting for link speed from IOP\n"); - return RC_RTN_NO_FIRM_VER; - } - } - - strcpy (pFirmString, (PU8) p32); - return RC_RTN_NO_ERROR; -} - -/* -** ========================================================================= -** RCResetLANCard() -** -** ResourceFlags indicates whether to return buffer resource explicitly -** to host or keep and reuse. -** CallbackFunction (if not NULL) is the function to be called when -** reset is complete. -** If CallbackFunction is NULL, ReturnAddr will have a 1 placed in it when -** reset is done (if not NULL). -** -** ========================================================================= -*/ -RC_RETURN -RCResetLANCard (struct net_device * dev, U16 ResourceFlags, PU32 ReturnAddr, - PFNCALLBACK CallbackFunction) -{ - unsigned long off; - PU32 pMsg; - PPAB pPab = ((PDPA) dev->priv)->pPab; - long timeout = 0; - - if (pPab == NULL) - return RC_RTN_ADPTR_NOT_REGISTERED; - - off = pPab->p_atu->InQueue; /* get addresss of message */ - - if (0xFFFFFFFF == off) - return RC_RTN_FREE_Q_EMPTY; - - pPab->pCallbackFunc = CallbackFunction; - - pMsg = (PU32) (pPab->pPci45LinBaseAddr + off); - - /* setup message */ - pMsg[0] = FOUR_WORD_MSG_SIZE | SGL_OFFSET_0; - pMsg[1] = I2O_LAN_RESET << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID; - pMsg[2] = DEFAULT_RECV_INIT_CONTEXT; - pMsg[3] = ResourceFlags << 16; /* resource flags */ - - pPab->p_atu->InQueue = off; /* send it to the I2O device */ - - if (CallbackFunction == (PFNCALLBACK) NULL) { - /* call RCProcI2OMsgQ() until something in pPab->pCallbackFunc - or until timer goes off */ - while (pPab->pCallbackFunc == (PFNCALLBACK) NULL) { - RCProcI2OMsgQ (dev); - udelay (1000); /* please don't hog the bus!!! */ - timeout++; - if (timeout > 10000) { - break; - } - } - if (ReturnAddr != (PU32) NULL) - *ReturnAddr = (U32) pPab->pCallbackFunc; - } - - return RC_RTN_NO_ERROR; -} - -/* -** ========================================================================= -** RCResetIOP() -** -** Send StatusGet Msg, wait for results return directly to buffer. -** -** ========================================================================= -*/ -RC_RETURN -RCResetIOP (struct net_device * dev) -{ - U32 msgOffset, timeout; - PU32 pMsg; - PPAB pPab = ((PDPA) dev->priv)->pPab; - volatile PU32 p32; - - msgOffset = pPab->p_atu->InQueue; - - if (msgOffset == 0xFFFFFFFF) { - return RC_RTN_FREE_Q_EMPTY; - } - - /* calc virtual address of msg - virtual already mapped to physical */ - pMsg = (PU32) (pPab->pPci45LinBaseAddr + msgOffset); - - pMsg[0] = NINE_WORD_MSG_SIZE | SGL_OFFSET_0; - pMsg[1] = I2O_EXEC_IOP_RESET << 24 | I2O_HOST_TID << 12 | I2O_IOP_TID; - pMsg[2] = 0; /* universal context */ - pMsg[3] = 0; /* universal context */ - pMsg[4] = 0; /* universal context */ - pMsg[5] = 0; /* universal context */ - /* phys address to return status - area right after PAB */ - pMsg[6] = pPab->outMsgBlockPhyAddr; - pMsg[7] = 0; - pMsg[8] = 1; /* return 1 byte */ - - /* virtual pointer to return buffer - clear first two dwords */ - p32 = (volatile PU32) pPab->pLinOutMsgBlock; - p32[0] = 0; - p32[1] = 0; - - /* post to Inbound Post Q */ - - pPab->p_atu->InQueue = msgOffset; - - /* wait for response */ - timeout = 1000000; - while (1) { - udelay (10); /* please don't hog the bus!!! */ - - if (p32[0] || p32[1]) - break; - - if (!timeout--) { - dprintk ("RCResetIOP timeout\n"); - return RC_RTN_MSG_REPLY_TIMEOUT; - } - } - return RC_RTN_NO_ERROR; -} - -/* -** ========================================================================= -** RCShutdownLANCard() -** -** ResourceFlags indicates whether to return buffer resource explicitly -** to host or keep and reuse. -** CallbackFunction (if not NULL) is the function to be called when -** shutdown is complete. -** If CallbackFunction is NULL, ReturnAddr will have a 1 placed in it when -** shutdown is done (if not NULL). -** -** ========================================================================= -*/ -RC_RETURN -RCShutdownLANCard (struct net_device * dev, U16 ResourceFlags, - PU32 ReturnAddr, PFNCALLBACK CallbackFunction) -{ - volatile PU32 pMsg; - U32 off; - PPAB pPab = ((PDPA) dev->priv)->pPab; - long timeout = 0; - - if (pPab == NULL) - return RC_RTN_ADPTR_NOT_REGISTERED; - - off = pPab->p_atu->InQueue; /* get addresss of message */ - - if (0xFFFFFFFF == off) - return RC_RTN_FREE_Q_EMPTY; - - pPab->pCallbackFunc = CallbackFunction; - - pMsg = (PU32) (pPab->pPci45LinBaseAddr + off); - - /* setup message */ - pMsg[0] = FOUR_WORD_MSG_SIZE | SGL_OFFSET_0; - pMsg[1] = - I2O_LAN_SHUTDOWN << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID; - pMsg[2] = DEFAULT_RECV_INIT_CONTEXT; - pMsg[3] = ResourceFlags << 16; /* resource flags */ - - pPab->p_atu->InQueue = off; /* send it to the I2O device */ - - if (CallbackFunction == (PFNCALLBACK) NULL) { - /* call RCProcI2OMsgQ() until something in pPab->pCallbackFunc - or until timer goes off */ - while (pPab->pCallbackFunc == (PFNCALLBACK) NULL) { - RCProcI2OMsgQ (dev); - udelay (1000); /* please don't hog the bus!!! */ - timeout++; - if (timeout > 10000) { - printk (KERN_WARNING - "(rcpci45 driver:) RCShutdownLANCard(): timeout\n"); - break; - } - } - if (ReturnAddr != (PU32) NULL) - *ReturnAddr = (U32) pPab->pCallbackFunc; - } - return RC_RTN_NO_ERROR; -} - -/* -** ========================================================================= -** RCSetRavlinIPandMask() -** -** Set the Ravlin 45/PCI cards IP address and network mask. -** -** IP address and mask must be in network byte order. -** For example, IP address 1.2.3.4 and mask 255.255.255.0 would be -** 0x04030201 and 0x00FFFFFF on a little endian machine. -** -** ========================================================================= -*/ -RC_RETURN -RCSetRavlinIPandMask (struct net_device * dev, U32 ipAddr, U32 netMask) -{ - volatile PU32 pMsg; - U32 off; - PPAB pPab = ((PDPA) dev->priv)->pPab; - - if (pPab == NULL) - return RC_RTN_ADPTR_NOT_REGISTERED; - - off = pPab->p_atu->InQueue; /* get addresss of message */ - - if (0xFFFFFFFF == off) - return RC_RTN_FREE_Q_EMPTY; - - pMsg = (PU32) (pPab->pPci45LinBaseAddr + off); - - /* setup private message */ - pMsg[0] = SEVEN_WORD_MSG_SIZE | SGL_OFFSET_0; - pMsg[1] = I2O_PRIVATE << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID; - pMsg[2] = 0; /* initiator context */ - pMsg[3] = 0x219; /* transaction context */ - pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_SET_IP_AND_MASK; - pMsg[5] = ipAddr; - pMsg[6] = netMask; - - pPab->p_atu->InQueue = off; /* send it to the I2O device */ - return RC_RTN_NO_ERROR; - -} - -/* -** ========================================================================= -** RCGetRavlinIPandMask() -** -** get the IP address and MASK from the card -** -** ========================================================================= -*/ -RC_RETURN -RCGetRavlinIPandMask (struct net_device * dev, PU32 pIpAddr, PU32 pNetMask, - PFNWAITCALLBACK WaitCallback) -{ - unsigned timeout; - U32 off; - PU32 pMsg, p32; - PPAB pPab = ((PDPA) dev->priv)->pPab; - PATU p_atu; - - dprintk - ("RCGetRavlinIPandMask: pIpAddr is 0x%08ulx, *IpAddr is 0x%08ulx\n", - (u32) pIpAddr, *pIpAddr); - - if (pPab == NULL) - return RC_RTN_ADPTR_NOT_REGISTERED; - - p_atu = pPab->p_atu; - off = p_atu->InQueue; /* get addresss of message */ - - if (0xFFFFFFFF == off) - return RC_RTN_FREE_Q_EMPTY; - - p32 = (volatile PU32) pPab->pLinOutMsgBlock; - *p32 = 0xFFFFFFFF; - - pMsg = (PU32) (pPab->pPci45LinBaseAddr + off); - - dprintk - ("RCGetRavlinIPandMask: p_atu 0x%08ulx, off 0x%08ulx, p32 0x%08ulx\n", - (u32) p_atu, off, (u32) p32); - /* setup private message */ - pMsg[0] = FIVE_WORD_MSG_SIZE | SGL_OFFSET_0; - pMsg[1] = I2O_PRIVATE << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID; - pMsg[2] = 0; /* initiator context */ - pMsg[3] = 0x218; /* transaction context */ - pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_GET_IP_AND_MASK; - pMsg[5] = pPab->outMsgBlockPhyAddr; - - p_atu->InQueue = off; /* send it to the I2O device */ - dprintk - ("RCGetRavlinIPandMask: p_atu 0x%08ulx, off 0x%08ulx, p32 0x%08ulx\n", - (u32) p_atu, off, (u32) p32); - - /* wait for the rcpci45 board to update the info */ - timeout = 100000; - while (0xffffffff == *p32) { - if (WaitCallback) - (*WaitCallback) (); - - udelay (10); - - if (!timeout--) { - dprintk ("RCGetRavlinIPandMask: Timeout\n"); - return RC_RTN_MSG_REPLY_TIMEOUT; - } - } - - dprintk - ("RCGetRavlinIPandMask: after time out\np32[0] (IpAddr) 0x%08ulx, p32[1] (IPmask) 0x%08ulx\n", - p32[0], p32[1]); - - /* send IP and mask to user's space */ - *pIpAddr = p32[0]; - *pNetMask = p32[1]; - - dprintk - ("RCGetRavlinIPandMask: pIpAddr is 0x%08ulx, *IpAddr is 0x%08ulx\n", - (u32) pIpAddr, *pIpAddr); - - return RC_RTN_NO_ERROR; -} - -/* -** ///////////////////////////////////////////////////////////////////////// -** ///////////////////////////////////////////////////////////////////////// -** -** local functions -** -** ///////////////////////////////////////////////////////////////////////// -** ///////////////////////////////////////////////////////////////////////// -*/ - -/* -** ========================================================================= -** SendI2OOutboundQInitMsg() -** -** ========================================================================= -*/ -static int -SendI2OOutboundQInitMsg (PPAB pPab) -{ - U32 msgOffset, timeout, phyOutQFrames, i; - volatile PU32 pMsg; - volatile PU32 p32; - - msgOffset = pPab->p_atu->InQueue; - - if (msgOffset == 0xFFFFFFFF) { - dprintk ("SendI2OOutboundQInitMsg(): Inbound Free Q empty!\n"); - return RC_RTN_FREE_Q_EMPTY; - } - - /* calc virtual address of msg - virtual already mapped to physical */ - pMsg = (PU32) (pPab->pPci45LinBaseAddr + msgOffset); - - dprintk - ("SendI2OOutboundQInitMsg - pMsg = 0x%08ulx, InQ msgOffset = 0x%08ulx\n", - (u32) pMsg, msgOffset); - - pMsg[0] = EIGHT_WORD_MSG_SIZE | TRL_OFFSET_6; - pMsg[1] = - I2O_EXEC_OUTBOUND_INIT << 24 | I2O_HOST_TID << 12 | I2O_IOP_TID; - pMsg[2] = DEFAULT_RECV_INIT_CONTEXT; - pMsg[3] = 0x106; /* transaction context */ - pMsg[4] = 4096; /* Host page frame size */ - pMsg[5] = MSG_FRAME_SIZE << 16 | 0x80; /* outbound msg frame size and Initcode */ - pMsg[6] = 0xD0000004; /* simple sgl element LE, EOB */ - /* phys address to return status - area right after PAB */ - pMsg[7] = pPab->outMsgBlockPhyAddr; - - /* virtual pointer to return buffer - clear first two dwords */ - p32 = (PU32) pPab->pLinOutMsgBlock; - p32[0] = 0; - - /* post to Inbound Post Q */ - pPab->p_atu->InQueue = msgOffset; - - /* wait for response */ - timeout = 100000; - while (1) { - udelay (10); /* please don't hog the bus!!! */ - - if (p32[0]) - break; - - if (!timeout--) { - dprintk - ("Timeout wait for InitOutQ InPrgress status from IOP\n"); - return RC_RTN_NO_I2O_STATUS; - } - } - - timeout = 100000; - while (1) { - udelay (10); /* please don't hog the bus!!! */ - - if (p32[0] == I2O_EXEC_OUTBOUND_INIT_COMPLETE) - break; - - if (!timeout--) { - dprintk - ("Timeout wait for InitOutQ Complete status from IOP\n"); - return RC_RTN_NO_I2O_STATUS; - } - } - - /* load PCI outbound free Q with MF physical addresses */ - phyOutQFrames = pPab->outMsgBlockPhyAddr; - - for (i = 0; i < NMBR_MSG_FRAMES; i++) { - pPab->p_atu->OutQueue = phyOutQFrames; - phyOutQFrames += MSG_FRAME_SIZE; - } - return RC_RTN_NO_ERROR; -} - -/* -** ========================================================================= -** GetI2OStatus() -** -** Send StatusGet Msg, wait for results return directly to buffer. -** -** ========================================================================= -*/ -static int -GetI2OStatus (PPAB pPab) -{ - U32 msgOffset, timeout; - PU32 pMsg; - volatile PU32 p32; - - msgOffset = pPab->p_atu->InQueue; - dprintk ("GetI2OStatus: msg offset = 0x%x\n", msgOffset); - if (msgOffset == 0xFFFFFFFF) { - dprintk ("GetI2OStatus(): Inbound Free Q empty!\n"); - return RC_RTN_FREE_Q_EMPTY; - } - - /* calc virtual address of msg - virtual already mapped to physical */ - pMsg = (PU32) (pPab->pPci45LinBaseAddr + msgOffset); - - pMsg[0] = NINE_WORD_MSG_SIZE | SGL_OFFSET_0; - pMsg[1] = I2O_EXEC_STATUS_GET << 24 | I2O_HOST_TID << 12 | I2O_IOP_TID; - pMsg[2] = 0; /* universal context */ - pMsg[3] = 0; /* universal context */ - pMsg[4] = 0; /* universal context */ - pMsg[5] = 0; /* universal context */ - /* phys address to return status - area right after PAB */ - pMsg[6] = pPab->outMsgBlockPhyAddr; - pMsg[7] = 0; - pMsg[8] = 88; /* return 88 bytes */ - - /* virtual pointer to return buffer - clear first two dwords */ - p32 = (volatile PU32) pPab->pLinOutMsgBlock; - p32[0] = 0; - p32[1] = 0; - - dprintk - ("GetI2OStatus - pMsg:0x%08ulx, msgOffset:0x%08ulx, [1]:0x%08ulx, [6]:0x%08ulx\n", - (u32) pMsg, msgOffset, pMsg[1], pMsg[6]); - - /* post to Inbound Post Q */ - pPab->p_atu->InQueue = msgOffset; - - dprintk ("Return status to p32 = 0x%08ulx\n", (u32) p32); - - /* wait for response */ - timeout = 1000000; - while (1) { - udelay (10); /* please don't hog the bus!!! */ - - if (p32[0] && p32[1]) - break; - - if (!timeout--) { - dprintk ("Timeout waiting for status from IOP\n"); - dprintk ("0x%08ulx:0x%08ulx:0x%08ulx:0x%08ulx\n", - p32[0], p32[1], p32[2], p32[3]); - dprintk ("0x%08ulx:0x%08ulx:0x%08ulx:0x%08ulx\n", - p32[4], p32[5], p32[6], p32[7]); - dprintk ("0x%08ulx:0x%08ulx:0x%08ulx:0x%08ulx\n", - p32[8], p32[9], p32[10], p32[11]); - return RC_RTN_NO_I2O_STATUS; - } - } - - dprintk ("0x%08ulx:0x%08ulx:0x%08ulx:0x%08ulx\n", p32[0], p32[1], - p32[2], p32[3]); - dprintk ("0x%08ulx:0x%08ulx:0x%08ulx:0x%08ulx\n", p32[4], p32[5], - p32[6], p32[7]); - dprintk ("0x%08ulx:0x%08ulx:0x%08ulx:0x%08ulx\n", p32[8], p32[9], - p32[10], p32[11]); - /* get IOP state */ - pPab->IOPState = ((volatile PU8) p32)[10]; - pPab->InboundMFrameSize = ((volatile PU16) p32)[6]; - - dprintk ("IOP state 0x%02x InFrameSize = 0x%04x\n", - pPab->IOPState, pPab->InboundMFrameSize); - return RC_RTN_NO_ERROR; -} - -/* -** ========================================================================= -** SendEnableSysMsg() -** -** -** ========================================================================= -*/ -static int -SendEnableSysMsg (PPAB pPab) -{ - U32 msgOffset; - volatile PU32 pMsg; - - msgOffset = pPab->p_atu->InQueue; - - if (msgOffset == 0xFFFFFFFF) { - dprintk ("SendEnableSysMsg(): Inbound Free Q empty!\n"); - return RC_RTN_FREE_Q_EMPTY; - } - - /* calc virtual address of msg - virtual already mapped to physical */ - pMsg = (PU32) (pPab->pPci45LinBaseAddr + msgOffset); - - dprintk - ("SendEnableSysMsg - pMsg = 0x%08ulx, InQ msgOffset = 0x%08ulx\n", - (u32) pMsg, msgOffset); - - pMsg[0] = FOUR_WORD_MSG_SIZE | SGL_OFFSET_0; - pMsg[1] = I2O_EXEC_SYS_ENABLE << 24 | I2O_HOST_TID << 12 | I2O_IOP_TID; - pMsg[2] = DEFAULT_RECV_INIT_CONTEXT; - pMsg[3] = 0x110; /* transaction context */ - pMsg[4] = 0x50657465; /* RedCreek Private */ - - /* post to Inbound Post Q */ - pPab->p_atu->InQueue = msgOffset; - - return RC_RTN_NO_ERROR; -} - -/* -** ========================================================================= -** FillI2OMsgFromTCB() -** -** inputs pMsgU32 - virtual pointer (mapped to physical) of message frame -** pXmitCntrlBlock - pointer to caller buffer control block. -** -** fills in LAN SGL after Transaction Control Word or Bucket Count. -** ========================================================================= -*/ -static int -FillI2OMsgSGLFromTCB (PU32 pMsgFrame, PRCTCB pTransCtrlBlock) -{ - unsigned int nmbrBuffers, nmbrSeg, nmbrDwords, context, flags; - PU32 pTCB, pMsg; - - /* SGL element flags */ -#define EOB 0x40000000 -#define LE 0x80000000 -#define SIMPLE_SGL 0x10000000 -#define BC_PRESENT 0x01000000 - - pTCB = (PU32) pTransCtrlBlock; - pMsg = pMsgFrame; - nmbrDwords = 0; - - dprintk ("FillI2OMsgSGLFromTCBX\n"); - dprintk ("TCB 0x%08ulx:0x%08ulx:0x%08ulx:0x%08ulx:0x%08ulx\n", - pTCB[0], pTCB[1], pTCB[2], pTCB[3], pTCB[4]); - dprintk ("pTCB 0x%08ulx, pMsg 0x%08ulx\n", (u32) pTCB, (u32) pMsg); - - nmbrBuffers = *pTCB++; - - if (!nmbrBuffers) { - return -1; - } - - do { - context = *pTCB++; /* buffer tag (context) */ - nmbrSeg = *pTCB++; /* number of segments */ - - if (!nmbrSeg) { - return -1; - } - - flags = SIMPLE_SGL | BC_PRESENT; - - if (1 == nmbrSeg) { - flags |= EOB; - - if (1 == nmbrBuffers) - flags |= LE; - } - - /* 1st SGL buffer element has context */ - pMsg[0] = pTCB[0] | flags; /* send over count (segment size) */ - pMsg[1] = context; - pMsg[2] = pTCB[1]; /* send buffer segment physical address */ - nmbrDwords += 3; - pMsg += 3; - pTCB += 2; - - if (--nmbrSeg) { - do { - flags = SIMPLE_SGL; - - if (1 == nmbrSeg) { - flags |= EOB; - - if (1 == nmbrBuffers) - flags |= LE; - } - - pMsg[0] = pTCB[0] | flags; /* send over count */ - pMsg[1] = pTCB[1]; /* send buffer segment physical address */ - nmbrDwords += 2; - pTCB += 2; - pMsg += 2; - - } while (--nmbrSeg); - } - - } while (--nmbrBuffers); - - return nmbrDwords; -} - -/* -** ========================================================================= -** ProcessOutboundI2OMsg() -** -** process I2O reply message -** * change to msg structure * -** ========================================================================= -*/ -static void -ProcessOutboundI2OMsg (PPAB pPab, U32 phyAddrMsg) -{ - PU8 p8Msg; - PU32 p32; -/* U16 count; */ - - p8Msg = pPab->pLinOutMsgBlock + (phyAddrMsg - pPab->outMsgBlockPhyAddr); - p32 = (PU32) p8Msg; - - dprintk - ("VXD: ProcessOutboundI2OMsg - pPab 0x%08ulx, phyAdr 0x%08ulx, linAdr 0x%08ulx\n", - (u32) pPab, phyAddrMsg, (u32) p8Msg); - dprintk ("msg :0x%08ulx:0x%08ulx:0x%08ulx:0x%08ulx\n", p32[0], p32[1], - p32[2], p32[3]); - dprintk ("msg :0x%08ulx:0x%08ulx:0x%08ulx:0x%08ulx\n", p32[4], p32[5], - p32[6], p32[7]); - - if (p32[4] >> 24 != I2O_REPLY_STATUS_SUCCESS) { - dprintk ("Message reply status not success\n"); - return; - } - - switch (p8Msg[7]) { /* function code byte */ - case I2O_EXEC_SYS_TAB_SET: - msgFlag = 1; - dprintk ("Received I2O_EXEC_SYS_TAB_SET reply\n"); - break; - - case I2O_EXEC_HRT_GET: - msgFlag = 1; - dprintk ("Received I2O_EXEC_HRT_GET reply\n"); - break; - - case I2O_EXEC_LCT_NOTIFY: - msgFlag = 1; - dprintk ("Received I2O_EXEC_LCT_NOTIFY reply\n"); - break; - - case I2O_EXEC_SYS_ENABLE: - msgFlag = 1; - dprintk ("Received I2O_EXEC_SYS_ENABLE reply\n"); - break; - - default: - dprintk ("Received UNKNOWN reply\n"); - break; - } -} diff --git a/drivers/net/rclanmtl.h b/drivers/net/rclanmtl.h deleted file mode 100644 index 9488c0fd5..000000000 --- a/drivers/net/rclanmtl.h +++ /dev/null @@ -1,701 +0,0 @@ -/* -** ************************************************************************* -** -** -** R C L A N M T L . H $Revision: 6 $ -** -** -** RedCreek I2O LAN Message Transport Layer header file. -** -** --------------------------------------------------------------------- -** --- Copyright (c) 1997-1999, RedCreek Communications Inc. --- -** --- All rights reserved. --- -** --------------------------------------------------------------------- -** -** File Description: -** -** Header file for host I2O (Intelligent I/O) LAN message transport layer -** API and data types. -** -** This program is free software; you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation; either version 2 of the License, or -** (at your option) any later version. - -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. - -** You should have received a copy of the GNU General Public License -** along with this program; if not, write to the Free Software -** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -** -** ************************************************************************* -*/ - -#ifndef RCLANMTL_H -#define RCLANMTL_H - -/* Linux specific includes */ -#include -#ifdef RC_LINUX_MODULE /* linux modules need non-library version of string functions */ -#include -#else -#include -#endif -#include /* for udelay() */ - -#include -#include -#include -#include - -#include - -/* Debug stuff. Define for debug output */ -#undef RCDEBUG - -#ifdef RCDEBUG -#define dprintk(args...) printk(KERN_DEBUG "rc: " args) -#else -#define dprintk(args...) { } -#endif - -/* Typedefs */ - - /* scalar data types */ -typedef __u8 U8; -typedef __u16 U16; -typedef __u32 U32; -typedef __u8 *PU8; -typedef __u16 *PU16; -typedef __u32 *PU32; -typedef unsigned long BF; -typedef int RC_RETURN; - - /* - ** type PFNWAITCALLBACK - ** - ** pointer to void function - type used for WaitCallback in some functions - */ -typedef void (*PFNWAITCALLBACK) (void); /* void argument avoids compiler complaint */ - - /* - ** type PFNTXCALLBACK - ** - ** Pointer to user's transmit callback function. This user function is - ** called from RCProcI2OMsgQ() when packet have been transmitted from buffers - ** given in the RCI2OSendPacket() function. BufferContext is a pointer to - ** an array of 32 bit context values. These are the values the user assigned - ** and passed in the TCB to the RCI2OSendPacket() function. PcktCount - ** indicates the number of buffer context values in the BufferContext[] array. - ** The User's TransmitCallbackFunction should recover (put back in free queue) - ** the packet buffers associated with the buffer context values. - */ -typedef void (*PFNTXCALLBACK) (U32 Status, - U16 PcktCount, - PU32 BufferContext, struct net_device *); - - /* - ** type PFNRXCALLBACK - ** - ** Pointer to user's receive callback function. This user function - ** is called from RCProcI2OMsgQ() when packets have been received into - ** previously posted packet buffers throught the RCPostRecvBuffers() function. - ** The received callback function should process the Packet Descriptor Block - ** pointed to by PacketDescBlock. See Packet Decription Block below. - */ -typedef void (*PFNRXCALLBACK) (U32 Status, - U8 PktCount, - U32 BucketsRemain, - PU32 PacketDescBlock, struct net_device *); - - /* - ** type PFNCALLBACK - ** - ** Pointer to user's generic callback function. This user function - ** can be passed to LANReset or LANShutdown and is called when the - ** the reset or shutdown is complete. - ** Param1 and Param2 are invalid for LANReset and LANShutdown. - */ -typedef void (*PFNCALLBACK) (U32 Status, - U32 Param1, U32 Param2, struct net_device * dev); - -/* -** Message Unit CSR definitions for RedCreek PCI45 board -*/ -typedef struct tag_rcatu { - volatile unsigned long APICRegSel; /* APIC Register Select */ - volatile unsigned long reserved0; - volatile unsigned long APICWinReg; /* APIC Window Register */ - volatile unsigned long reserved1; - volatile unsigned long InMsgReg0; /* inbound message register 0 */ - volatile unsigned long InMsgReg1; /* inbound message register 1 */ - volatile unsigned long OutMsgReg0; /* outbound message register 0 */ - volatile unsigned long OutMsgReg1; /* outbound message register 1 */ - volatile unsigned long InDoorReg; /* inbound doorbell register */ - volatile unsigned long InIntStat; /* inbound interrupt status register */ - volatile unsigned long InIntMask; /* inbound interrupt mask register */ - volatile unsigned long OutDoorReg; /* outbound doorbell register */ - volatile unsigned long OutIntStat; /* outbound interrupt status register */ - volatile unsigned long OutIntMask; /* outbound interrupt mask register */ - volatile unsigned long reserved2; - volatile unsigned long reserved3; - volatile unsigned long InQueue; /* inbound queue port */ - volatile unsigned long OutQueue; /* outbound queue port */ - volatile unsigned long reserved4; - volatile unsigned long reserver5; - /* RedCreek extension */ - volatile unsigned long EtherMacLow; - volatile unsigned long EtherMacHi; - volatile unsigned long IPaddr; - volatile unsigned long IPmask; -} *PATU; - - /* - ** typedef PAB - ** - ** PCI Adapter Block - holds instance specific information. - */ -typedef struct { - PATU p_atu; /* ptr to ATU register block */ - PU8 pPci45LinBaseAddr; - PU8 pLinOutMsgBlock; - U32 outMsgBlockPhyAddr; - PFNTXCALLBACK pTransCallbackFunc; - PFNRXCALLBACK pRecvCallbackFunc; - PFNCALLBACK pRebootCallbackFunc; - PFNCALLBACK pCallbackFunc; - U16 IOPState; - U16 InboundMFrameSize; -} *PPAB; - -/* - * Driver Private Area, DPA. - */ -typedef struct { - U8 id; /* the AdapterID */ - - /* These two field are basically for the RCioctl function. - * I could not determine if they could be avoided. (RAA)*/ - U32 pci_addr; /* the pci address of the adapter */ - U32 pci_addr_len; - - struct pci_dev *pci_dev; - struct timer_list timer; /* timer */ - struct net_device_stats stats; /* the statistics structure */ - unsigned long numOutRcvBuffers; /* number of outstanding receive buffers */ - unsigned char shutdown; - unsigned char reboot; - unsigned char nexus; - PU8 msgbuf; /* Pointer to Lan Api Private Area */ - dma_addr_t msgbuf_dma; - PPAB pPab; /* Pointer to the PCI Adapter Block */ -} *PDPA; - -/* PCI/45 Configuration space values */ -#define RC_PCI45_VENDOR_ID 0x4916 -#define RC_PCI45_DEVICE_ID 0x1960 - - /* RedCreek API function return values */ -#define RC_RTN_NO_ERROR 0 -#define RC_RTN_I2O_NOT_INIT 1 -#define RC_RTN_FREE_Q_EMPTY 2 -#define RC_RTN_TCB_ERROR 3 -#define RC_RTN_TRANSACTION_ERROR 4 -#define RC_RTN_ADAPTER_ALREADY_INIT 5 -#define RC_RTN_MALLOC_ERROR 6 -#define RC_RTN_ADPTR_NOT_REGISTERED 7 -#define RC_RTN_MSG_REPLY_TIMEOUT 8 -#define RC_RTN_NO_I2O_STATUS 9 -#define RC_RTN_NO_FIRM_VER 10 -#define RC_RTN_NO_LINK_SPEED 11 - -/* Driver capability flags */ -#define WARM_REBOOT_CAPABLE 0x01 - -/* -** Status - Transmit and Receive callback status word -** -** A 32 bit Status is returned to the TX and RX callback functions. This value -** contains both the reply status and the detailed status as follows: -** -** 32 24 16 0 -** +------+------+------------+ -** | Reply| | Detailed | -** |Status| 0 | Status | -** +------+------+------------+ -** -** Reply Status and Detailed Status of zero indicates No Errors. -*/ - /* reply message status defines */ -#define I2O_REPLY_STATUS_SUCCESS 0x00 -#define I2O_REPLY_STATUS_ABORT_NO_DATA_TRANSFER 0x02 -#define I2O_REPLY_STATUS_TRANSACTION_ERROR 0x0A - -/* DetailedStatusCode defines */ -#define I2O_LAN_DSC_SUCCESS 0x0000 -#define I2O_LAN_DSC_DEVICE_FAILURE 0x0001 -#define I2O_LAN_DSC_DESTINATION_NOT_FOUND 0x0002 -#define I2O_LAN_DSC_TRANSMIT_ERROR 0x0003 -#define I2O_LAN_DSC_TRANSMIT_ABORTED 0x0004 -#define I2O_LAN_DSC_RECEIVE_ERROR 0x0005 -#define I2O_LAN_DSC_RECEIVE_ABORTED 0x0006 -#define I2O_LAN_DSC_DMA_ERROR 0x0007 -#define I2O_LAN_DSC_BAD_PACKET_DETECTED 0x0008 -#define I2O_LAN_DSC_OUT_OF_MEMORY 0x0009 -#define I2O_LAN_DSC_BUCKET_OVERRUN 0x000A -#define I2O_LAN_DSC_IOP_INTERNAL_ERROR 0x000B -#define I2O_LAN_DSC_CANCELED 0x000C -#define I2O_LAN_DSC_INVALID_TRANSACTION_CONTEXT 0x000D -#define I2O_LAN_DSC_DESTINATION_ADDRESS_DETECTED 0x000E -#define I2O_LAN_DSC_DESTINATION_ADDRESS_OMITTED 0x000F -#define I2O_LAN_DSC_PARTIAL_PACKET_RETURNED 0x0010 - -/* -** Packet Description Block (Received packets) -** -** A pointer to this block structure is returned to the ReceiveCallback -** function. It contains the list of packet buffers which have either been -** filled with a packet or returned to host due to a LANReset function. -** Currently there will only be one packet per receive bucket (buffer) posted. -** -** 32 24 0 -** +-----------------------+ -\ -** | Buffer 1 Context | \ -** +-----------------------+ \ -** | 0xC0000000 | / First Bucket Descriptor -** +-----+-----------------+ / -** | 0 | packet 1 length | / -** +-----------------------+ -\ -** | Buffer 2 Context | \ -** +-----------------------+ \ -** | 0xC0000000 | / Second Bucket Descriptor -** +-----+-----------------+ / -** | 0 | packet 2 length | / -** +-----+-----------------+ - -** | ... | ----- more bucket descriptors -** +-----------------------+ -\ -** | Buffer n Context | \ -** +-----------------------+ \ -** | 0xC0000000 | / Last Bucket Descriptor -** +-----+-----------------+ / -** | 0 | packet n length | / -** +-----+-----------------+ - -** -** Buffer Context values are those given to adapter in the TCB on calls to -** RCPostRecvBuffers(). -** -*/ - -/* -** Transaction Control Block (TCB) structure -** -** A structure like this is filled in by the user and passed by reference to -** RCI2OSendPacket() and RCPostRecvBuffers() functions. Minimum size is five -** 32-bit words for one buffer with one segment descriptor. -** MAX_NMBR_POST_BUFFERS_PER_MSG defines the maximum single segment buffers -** that can be described in a given TCB. -** -** 32 0 -** +-----------------------+ -** | Buffer Count | Number of buffers in the TCB -** +-----------------------+ -** | Buffer 1 Context | first buffer reference -** +-----------------------+ -** | Buffer 1 Seg Count | number of segments in buffer -** +-----------------------+ -** | Buffer 1 Seg Desc 1 | first segment descriptor (size, physical address) -** +-----------------------+ -** | ... | more segment descriptors (size, physical address) -** +-----------------------+ -** | Buffer 1 Seg Desc n | last segment descriptor (size, physical address) -** +-----------------------+ -** | Buffer 2 Context | second buffer reference -** +-----------------------+ -** | Buffer 2 Seg Count | number of segments in buffer -** +-----------------------+ -** | Buffer 2 Seg Desc 1 | segment descriptor (size, physical address) -** +-----------------------+ -** | ... | more segment descriptors (size, physical address) -** +-----------------------+ -** | Buffer 2 Seg Desc n | -** +-----------------------+ -** | ... | more buffer descriptor blocks ... -** +-----------------------+ -** | Buffer n Context | -** +-----------------------+ -** | Buffer n Seg Count | -** +-----------------------+ -** | Buffer n Seg Desc 1 | -** +-----------------------+ -** | ... | -** +-----------------------+ -** | Buffer n Seg Desc n | -** +-----------------------+ -** -** -** A TCB for one contigous packet buffer would look like the following: -** -** 32 0 -** +-----------------------+ -** | 1 | one buffer in the TCB -** +-----------------------+ -** | | user's buffer reference -** +-----------------------+ -** | 1 | one segment buffer -** +-----------------------+ _ -** | | size \ -** +-----------------------+ \ segment descriptor -** | | physical address of buffer / -** +-----------------------+ _/ -** -*/ - - /* Buffer Segment Descriptor */ -typedef struct { - U32 size; - U32 phyAddress; -} BSD, *PBSD; - -typedef PU32 PRCTCB; -/* -** ------------------------------------------------------------------------- -** Exported functions comprising the API to the LAN I2O message transport layer -** ------------------------------------------------------------------------- -*/ - - /* - ** InitRCI2OMsgLayer() - ** - ** Called once prior to using the I2O LAN message transport layer. User - ** provides both the physical and virual address of a locked page buffer - ** that is used as a private buffer for the RedCreek I2O message - ** transport layer. This buffer must be a contigous memory block of a - ** minimum of 16K bytes and long word aligned. The user also must provide - ** the base address of the RedCreek PCI adapter assigned by BIOS or operating - ** system. - ** - ** Inputs: dev - the net_device struct for the device. - ** TransmitCallbackFunction - address of user's TX callback function - ** ReceiveCallbackFunction - address of user's RX callback function - ** RebootCallbackFunction - address of user's reboot callback function - ** - */ -RC_RETURN RCInitI2OMsgLayer (struct net_device *dev, - PFNTXCALLBACK TransmitCallbackFunction, - PFNRXCALLBACK ReceiveCallbackFunction, - PFNCALLBACK RebootCallbackFunction); - - /* - ** RCSetRavlinIPandMask() - ** - ** Set the Ravlin 45/PCI cards IP address and network mask. - ** - ** IP address and mask must be in network byte order. - ** For example, IP address 1.2.3.4 and mask 255.255.255.0 would be - ** 0x04030201 and 0x00FFFFFF on a little endian machine. - ** - */ -RC_RETURN RCSetRavlinIPandMask (struct net_device *dev, U32 ipAddr, - U32 netMask); - -/* -** ========================================================================= -** RCGetRavlinIPandMask() -** -** get the IP address and MASK from the card -** -** ========================================================================= -*/ -RC_RETURN -RCGetRavlinIPandMask (struct net_device *dev, PU32 pIpAddr, PU32 pNetMask, - PFNWAITCALLBACK WaitCallback); - - /* - ** RCProcI2OMsgQ() - ** - ** Called from user's polling loop or Interrupt Service Routine for a PCI - ** interrupt from the RedCreek PCI adapter. User responsible for determining - ** and hooking the PCI interrupt. This function will call the registered - ** callback functions, TransmitCallbackFunction or ReceiveCallbackFunction, - ** if a TX or RX transaction has completed. - */ -irqreturn_t RCProcI2OMsgQ (struct net_device *dev); - - /* - ** Disable and Enable I2O interrupts. I2O interrupts are enabled at Init time - ** but can be disabled and re-enabled through these two function calls. - ** Packets will still be put into any posted received buffers and packets will - ** be sent through RCI2OSendPacket() functions. Disabling I2O interrupts - ** will prevent hardware interrupt to host even though the outbound I2O msg - ** queue is not emtpy. - */ -RC_RETURN RCEnableI2OInterrupts (struct net_device *dev); -RC_RETURN RCDisableI2OInterrupts (struct net_device *dev); - - /* - ** RCPostRecvBuffers() - ** - ** Post user's page locked buffers for use by the PCI adapter to - ** return ethernet packets received from the LAN. Transaction Control Block, - ** provided by user, contains buffer descriptor(s) which includes a buffer - ** context number along with buffer size and physical address. See TCB above. - ** The buffer context and actual packet length are returned to the - ** ReceiveCallbackFunction when packets have been received. Buffers posted - ** to the RedCreek adapter are considered owned by the adapter until the - ** context is return to user through the ReceiveCallbackFunction. - */ -RC_RETURN RCPostRecvBuffers (struct net_device *dev, - PRCTCB pTransactionCtrlBlock); -#define MAX_NMBR_POST_BUFFERS_PER_MSG 32 - - /* - ** RCI2OSendPacket() - ** - ** Send user's ethernet packet from a locked page buffer. - ** Packet must have full MAC header, however without a CRC. - ** Initiator context is a user provided value that is returned - ** to the TransmitCallbackFunction when packet buffer is free. - ** Transmit buffer are considered owned by the adapter until context's - ** returned to user through the TransmitCallbackFunction. - */ -RC_RETURN RCI2OSendPacket (struct net_device *dev, - U32 context, PRCTCB pTransactionCtrlBlock); - - /* Ethernet Link Statistics structure */ -typedef struct tag_RC_link_stats { - U32 TX_good; /* good transmit frames */ - U32 TX_maxcol; /* frames not TX due to MAX collisions */ - U32 TX_latecol; /* frames not TX due to late collisions */ - U32 TX_urun; /* frames not TX due to DMA underrun */ - U32 TX_crs; /* frames TX with lost carrier sense */ - U32 TX_def; /* frames deferred due to activity on link */ - U32 TX_singlecol; /* frames TX with one and only on collision */ - U32 TX_multcol; /* frames TX with more than one collision */ - U32 TX_totcol; /* total collisions detected during TX */ - U32 Rcv_good; /* good frames received */ - U32 Rcv_CRCerr; /* frames RX and discarded with CRC errors */ - U32 Rcv_alignerr; /* frames RX with alignment and CRC errors */ - U32 Rcv_reserr; /* good frames discarded due to no RX buffer */ - U32 Rcv_orun; /* RX frames lost due to FIFO overrun */ - U32 Rcv_cdt; /* RX frames with collision during RX */ - U32 Rcv_runt; /* RX frames shorter than 64 bytes */ -} RCLINKSTATS, *P_RCLINKSTATS; - - /* - ** RCGetLinkStatistics() - ** - ** Returns link statistics in user's structure at address StatsReturnAddr - ** If given, not NULL, the function WaitCallback is called during the wait - ** loop while waiting for the adapter to respond. - */ -RC_RETURN RCGetLinkStatistics (struct net_device *dev, - P_RCLINKSTATS StatsReturnAddr, - PFNWAITCALLBACK WaitCallback); - - /* - ** RCGetLinkStatus() - ** - ** Return link status, up or down, to user's location addressed by ReturnAddr. - ** If given, not NULL, the function WaitCallback is called during the wait - ** loop while waiting for the adapter to respond. - */ -RC_RETURN RCGetLinkStatus (struct net_device *dev, - PU32 pReturnStatus, PFNWAITCALLBACK WaitCallback); - - /* Link Status defines - value returned in pReturnStatus */ -#define RC_LAN_LINK_STATUS_DOWN 0 -#define RC_LAN_LINK_STATUS_UP 1 - - /* - ** RCGetMAC() - ** - ** Get the current MAC address assigned to user. RedCreek Ravlin 45/PCI - ** has two MAC addresses. One which is private to the PCI Card, and - ** another MAC which is given to the user as its link layer MAC address. The - ** adapter runs in promiscous mode because of the dual address requirement. - ** The MAC address is returned to the unsigned char array pointer to by mac. - */ -RC_RETURN RCGetMAC (struct net_device *dev, PFNWAITCALLBACK WaitCallback); - - /* - ** RCSetMAC() - ** - ** Set a new user port MAC address. This address will be returned on - ** subsequent RCGetMAC() calls. - */ -RC_RETURN RCSetMAC (struct net_device *dev, PU8 mac); - - /* - ** RCSetLinkSpeed() - ** - ** set adapter's link speed based on given input code. - */ -RC_RETURN RCSetLinkSpeed (struct net_device *dev, U16 LinkSpeedCode); - /* Set link speed codes */ -#define LNK_SPD_AUTO_NEG_NWAY 0 -#define LNK_SPD_100MB_FULL 1 -#define LNK_SPD_100MB_HALF 2 -#define LNK_SPD_10MB_FULL 3 -#define LNK_SPD_10MB_HALF 4 - - /* - ** RCGetLinkSpeed() - ** - ** Return link speed code. - */ - /* Return link speed codes */ -#define LNK_SPD_UNKNOWN 0 -#define LNK_SPD_100MB_FULL 1 -#define LNK_SPD_100MB_HALF 2 -#define LNK_SPD_10MB_FULL 3 -#define LNK_SPD_10MB_HALF 4 - -RC_RETURN -RCGetLinkSpeed (struct net_device *dev, PU32 pLinkSpeedCode, - PFNWAITCALLBACK WaitCallback); -/* -** ========================================================================= -** RCSetPromiscuousMode(struct net_device *dev, U16 Mode) -** -** Defined values for Mode: -** 0 - turn off promiscuous mode -** 1 - turn on promiscuous mode -** -** ========================================================================= -*/ -#define PROMISCUOUS_MODE_OFF 0 -#define PROMISCUOUS_MODE_ON 1 -RC_RETURN RCSetPromiscuousMode (struct net_device *dev, U16 Mode); -/* -** ========================================================================= -** RCGetPromiscuousMode(struct net_device *dev, PU32 pMode, PFNWAITCALLBACK WaitCallback) -** -** get promiscuous mode setting -** -** Possible return values placed in pMode: -** 0 = promisuous mode not set -** 1 = promisuous mode is set -** -** ========================================================================= -*/ -RC_RETURN -RCGetPromiscuousMode (struct net_device *dev, PU32 pMode, - PFNWAITCALLBACK WaitCallback); - -/* -** ========================================================================= -** RCSetBroadcastMode(struct net_device *dev, U16 Mode) -** -** Defined values for Mode: -** 0 - turn off promiscuous mode -** 1 - turn on promiscuous mode -** -** ========================================================================= -*/ -#define BROADCAST_MODE_OFF 0 -#define BROADCAST_MODE_ON 1 -RC_RETURN RCSetBroadcastMode (struct net_device *dev, U16 Mode); -/* -** ========================================================================= -** RCGetBroadcastMode(struct net_device *dev, PU32 pMode, PFNWAITCALLBACK WaitCallback) -** -** get broadcast mode setting -** -** Possible return values placed in pMode: -** 0 = broadcast mode not set -** 1 = broadcast mode is set -** -** ========================================================================= -*/ -RC_RETURN -RCGetBroadcastMode (struct net_device *dev, PU32 pMode, - PFNWAITCALLBACK WaitCallback); -/* -** ========================================================================= -** RCReportDriverCapability(struct net_device *dev, U32 capability) -** -** Currently defined bits: -** WARM_REBOOT_CAPABLE 0x01 -** -** ========================================================================= -*/ -RC_RETURN RCReportDriverCapability (struct net_device *dev, U32 capability); - -/* -** RCGetFirmwareVer() -** -** Return firmware version in the form "SoftwareVersion : Bt BootVersion" -** -** WARNING: user's space pointed to by pFirmString should be at least 60 bytes. -*/ -RC_RETURN -RCGetFirmwareVer (struct net_device *dev, PU8 pFirmString, - PFNWAITCALLBACK WaitCallback); - -/* -** ---------------------------------------------- -** LAN adapter Reset and Shutdown functions -** ---------------------------------------------- -*/ - /* resource flag bit assignments for RCResetLANCard() & RCShutdownLANCard() */ -#define RC_RESOURCE_RETURN_POSTED_RX_BUCKETS 0x0001 -#define RC_RESOURCE_RETURN_PEND_TX_BUFFERS 0x0002 - - /* - ** RCResetLANCard() - ** - ** Reset LAN card operation. Causes a software reset of the ethernet - ** controller and restarts the command and receive units. Depending on - ** the ResourceFlags given, the buffers are either returned to the - ** host with reply status of I2O_REPLY_STATUS_ABORT_NO_DATA_TRANSFER and - ** detailed status of I2O_LAN_DSC_CANCELED (new receive buffers must be - ** posted after issuing this) OR the buffers are kept and reused by - ** the ethernet controller. If CallbackFunction is not NULL, the function - ** will be called when the reset is complete. If the CallbackFunction is - ** NULL,a 1 will be put into the ReturnAddr after waiting for the reset - ** to complete (please disable I2O interrupts during this method). - ** Any outstanding transmit or receive buffers that are complete will be - ** returned via the normal reply messages before the requested resource - ** buffers are returned. - ** A call to RCPostRecvBuffers() is needed to return the ethernet to full - ** operation if the receive buffers were returned during LANReset. - ** Note: The IOP status is not affected by a LAN reset. - */ -RC_RETURN RCResetLANCard (struct net_device *dev, U16 ResourceFlags, - PU32 ReturnAddr, PFNCALLBACK CallbackFunction); - - /* - ** RCShutdownLANCard() - ** - ** Shutdown LAN card operation and put into an idle (suspended) state. - ** The LAN card is restarted with RCResetLANCard() function. - ** Depending on the ResourceFlags given, the buffers are either returned - ** to the host with reply status of I2O_REPLY_STATUS_ABORT_NO_DATA_TRANSFER - ** and detailed status of I2O_LAN_DSC_CANCELED (new receive buffers must be - ** posted after issuing this) OR the buffers are kept and reused by - ** the ethernet controller. If CallbackFunction is not NULL, the function - ** will be called when the reset is complete. If the CallbackFunction is - ** NULL,a 1 will be put into the ReturnAddr after waiting for the reset - ** to complete (please disable I2O interrupts during this method). - ** Any outstanding transmit or receive buffers that are complete will be - ** returned via the normal reply messages before the requested resource - ** buffers are returned. - ** Note: The IOP status is not affected by a LAN shutdown. - */ -RC_RETURN -RCShutdownLANCard (struct net_device *dev, U16 ResourceFlags, PU32 ReturnAddr, - PFNCALLBACK CallbackFunction); - - /* - ** RCResetIOP(); - ** Initializes IOPState to I2O_IOP_STATE_RESET. - ** Stops access to outbound message Q. - ** Discards any outstanding transmit or posted receive buffers. - ** Clears outbound message Q. - */ -RC_RETURN RCResetIOP (struct net_device *dev); - -#endif /* RCLANMTL_H */ diff --git a/drivers/net/rcpci45.c b/drivers/net/rcpci45.c deleted file mode 100644 index 76b63f31b..000000000 --- a/drivers/net/rcpci45.c +++ /dev/null @@ -1,1049 +0,0 @@ -/* -** -** RCpci45.c -** -** -** -** --------------------------------------------------------------------- -** --- Copyright (c) 1998, 1999, RedCreek Communications Inc. --- -** --- All rights reserved. --- -** --------------------------------------------------------------------- -** -** Written by Pete Popov and Brian Moyle. -** -** Known Problems -** -** None known at this time. -** -** This program is free software; you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation; either version 2 of the License, or -** (at your option) any later version. - -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. - -** You should have received a copy of the GNU General Public License -** along with this program; if not, write to the Free Software -** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -** -** Francois Romieu, Apr 2003: Converted to pci DMA mapping API. -** -** Pete Popov, Oct 2001: Fixed a few bugs to make the driver functional -** again. Note that this card is not supported or manufactured by -** RedCreek anymore. -** -** Rasmus Andersen, December 2000: Converted to new PCI API and general -** cleanup. -** -** Pete Popov, January 11,99: Fixed a couple of 2.1.x problems -** (virt_to_bus() not called), tested it under 2.2pre5 (as a module), and -** added a #define(s) to enable the use of the same file for both, the 2.0.x -** kernels as well as the 2.1.x. -** -** Ported to 2.1.x by Alan Cox 1998/12/9. -** -** Sometime in mid 1998, written by Pete Popov and Brian Moyle. -** -***************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include /* For NR_IRQS only. */ -#include -#include - -static char version[] __initdata = - "RedCreek Communications PCI linux driver version 2.21\n"; - -#define RC_LINUX_MODULE -#include "rclanmtl.h" -#include "rcif.h" - -#define RUN_AT(x) (jiffies + (x)) - -#define NEW_MULTICAST - -#define MAX_ETHER_SIZE 1520 -#define MAX_NMBR_RCV_BUFFERS 96 -#define RC_POSTED_BUFFERS_LOW_MARK MAX_NMBR_RCV_BUFFERS-16 -#define BD_SIZE 3 /* Bucket Descriptor size */ -#define BD_LEN_OFFSET 2 /* Bucket Descriptor offset to length field */ - -/* RedCreek LAN device Target ID */ -#define RC_LAN_TARGET_ID 0x10 -/* RedCreek's OSM default LAN receive Initiator */ -#define DEFAULT_RECV_INIT_CONTEXT 0xA17 - -/* minimum msg buffer size needed by the card - * Note that the size of this buffer is hard code in the - * ipsec card's firmware. Thus, the size MUST be a minimum - * of 16K. Otherwise the card will end up using memory - * that does not belong to it. - */ -#define MSG_BUF_SIZE 16384 - -/* 2003/04/20: I don't know about the hardware ability but the driver won't - * play safe with 64 bit addressing and DAC without NETIF_F_HIGHDMA doesn't - * really make sense anyway. Let's play safe - romieu. - */ -#define RCPCI45_DMA_MASK ((u64) 0xffffffff) - -static U32 DriverControlWord; - -static void rc_timer (unsigned long); - -static int RCopen (struct net_device *); -static int RC_xmit_packet (struct sk_buff *, struct net_device *); -static irqreturn_t RCinterrupt (int, void *, struct pt_regs *); -static int RCclose (struct net_device *dev); -static struct net_device_stats *RCget_stats (struct net_device *); -static int RCioctl (struct net_device *, struct ifreq *, int); -static int RCconfig (struct net_device *, struct ifmap *); -static void RCxmit_callback (U32, U16, PU32, struct net_device *); -static void RCrecv_callback (U32, U8, U32, PU32, struct net_device *); -static void RCreset_callback (U32, U32, U32, struct net_device *); -static void RCreboot_callback (U32, U32, U32, struct net_device *); -static int RC_allocate_and_post_buffers (struct net_device *, int); - -static struct pci_device_id rcpci45_pci_table[] = { - { PCI_VENDOR_ID_REDCREEK, PCI_DEVICE_ID_RC45, PCI_ANY_ID, PCI_ANY_ID,}, - {} -}; -MODULE_DEVICE_TABLE (pci, rcpci45_pci_table); -MODULE_LICENSE("GPL"); - -static void __devexit -rcpci45_remove_one (struct pci_dev *pdev) -{ - struct net_device *dev = pci_get_drvdata (pdev); - PDPA pDpa = dev->priv; - - RCResetIOP (dev); - unregister_netdev (dev); - free_irq (dev->irq, dev); - iounmap ((void *) dev->base_addr); - pci_release_regions (pdev); - pci_free_consistent (pdev, MSG_BUF_SIZE, pDpa->msgbuf, - pDpa->msgbuf_dma); - if (pDpa->pPab) - kfree (pDpa->pPab); - free_netdev (dev); - pci_set_drvdata (pdev, NULL); -} - -static int -rcpci45_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) -{ - unsigned long *vaddr; - PDPA pDpa; - int error; - static int card_idx = -1; - struct net_device *dev; - unsigned long pci_start, pci_len; - - card_idx++; - - /* - * Allocate and fill new device structure. - * We need enough for struct net_device plus DPA plus the LAN - * API private area, which requires a minimum of 16KB. The top - * of the allocated area will be assigned to struct net_device; - * the next chunk will be assigned to DPA; and finally, the rest - * will be assigned to the LAN API layer. - */ - - dev = alloc_etherdev(sizeof(*pDpa)); - if (!dev) { - printk (KERN_ERR - "(rcpci45 driver:) alloc_etherdev alloc failed\n"); - error = -ENOMEM; - goto err_out; - } - - SET_MODULE_OWNER(dev); - SET_NETDEV_DEV(dev, &pdev->dev); - - error = pci_enable_device (pdev); - if (error) { - printk (KERN_ERR - "(rcpci45 driver:) %d: pci enable device error\n", - card_idx); - goto err_out; - } - pci_start = pci_resource_start (pdev, 0); - pci_len = pci_resource_len (pdev, 0); - printk("pci_start %lx pci_len %lx\n", pci_start, pci_len); - - pci_set_drvdata (pdev, dev); - - pDpa = dev->priv; - pDpa->id = card_idx; - pDpa->pci_dev = pdev; - pDpa->pci_addr = pci_start; - - if (!pci_start || !(pci_resource_flags (pdev, 0) & IORESOURCE_MEM)) { - printk (KERN_ERR - "(rcpci45 driver:) No PCI mem resources! Aborting\n"); - error = -EBUSY; - goto err_out_free_dev; - } - - /* - * pDpa->msgbuf is where the card will dma the I2O - * messages. Thus, we need contiguous physical pages of memory. - * 2003/04/20: pci_alloc_consistent() provides well over the needed - * alignment on a 256 bytes boundary for the LAN API private area. - * Thus it isn't needed anymore to align it by hand. - */ - pDpa->msgbuf = pci_alloc_consistent (pdev, MSG_BUF_SIZE, - &pDpa->msgbuf_dma); - if (!pDpa->msgbuf) { - printk (KERN_ERR "(rcpci45 driver:) \ - Could not allocate %d byte memory for the \ - private msgbuf!\n", MSG_BUF_SIZE); - error = -ENOMEM; - goto err_out_free_dev; - } - - /* The adapter is accessible through memory-access read/write, not - * I/O read/write. Thus, we need to map it to some virtual address - * area in order to access the registers as normal memory. - */ - error = pci_request_regions (pdev, dev->name); - if (error) - goto err_out_free_msgbuf; - - error = pci_set_dma_mask (pdev, RCPCI45_DMA_MASK); - if (error) { - printk (KERN_ERR - "(rcpci45 driver:) pci_set_dma_mask failed!\n"); - goto err_out_free_region; - } - - vaddr = (ulong *) ioremap (pci_start, pci_len); - if (!vaddr) { - printk (KERN_ERR - "(rcpci45 driver:) \ - Unable to remap address range from %lu to %lu\n", - pci_start, pci_start + pci_len); - error = -EIO; - goto err_out_free_region; - } - - dev->base_addr = (unsigned long) vaddr; - dev->irq = pdev->irq; - dev->open = &RCopen; - dev->hard_start_xmit = &RC_xmit_packet; - dev->stop = &RCclose; - dev->get_stats = &RCget_stats; - dev->do_ioctl = &RCioctl; - dev->set_config = &RCconfig; - - if ((error = register_netdev(dev))) - goto err_out_iounmap; - - return 0; /* success */ - -err_out_iounmap: - iounmap((void *) dev->base_addr); -err_out_free_region: - pci_release_regions (pdev); -err_out_free_msgbuf: - pci_free_consistent (pdev, MSG_BUF_SIZE, pDpa->msgbuf, - pDpa->msgbuf_dma); -err_out_free_dev: - free_netdev (dev); -err_out: - card_idx--; - return error; -} - -static struct pci_driver rcpci45_driver = { - .name = "rcpci45", - .id_table = rcpci45_pci_table, - .probe = rcpci45_init_one, - .remove = __devexit_p(rcpci45_remove_one), -}; - -static int __init -rcpci_init_module (void) -{ - int rc = pci_module_init (&rcpci45_driver); - if (!rc) - printk (KERN_ERR "%s", version); - return rc; -} - -static int -RCopen (struct net_device *dev) -{ - int post_buffers = MAX_NMBR_RCV_BUFFERS; - PDPA pDpa = dev->priv; - int count = 0; - int requested = 0; - int error; - - if (pDpa->nexus) { - /* This is not the first time RCopen is called. Thus, - * the interface was previously opened and later closed - * by RCclose(). RCclose() does a Shutdown; to wake up - * the adapter, a reset is mandatory before we can post - * receive buffers. However, if the adapter initiated - * a reboot while the interface was closed -- and interrupts - * were turned off -- we need will need to reinitialize - * the adapter, rather than simply waking it up. - */ - printk (KERN_INFO "Waking up adapter...\n"); - RCResetLANCard (dev, 0, 0, 0); - } else { - pDpa->nexus = 1; - /* - * RCInitI2OMsgLayer is done only once, unless the - * adapter was sent a warm reboot - */ - error = RCInitI2OMsgLayer (dev, (PFNTXCALLBACK) RCxmit_callback, - (PFNRXCALLBACK) RCrecv_callback, - (PFNCALLBACK) RCreboot_callback); - if (error) { - printk (KERN_ERR "%s: Unable to init msg layer (%x)\n", - dev->name, error); - goto err_out; - } - if ((error = RCGetMAC (dev, NULL))) { - printk (KERN_ERR "%s: Unable to get adapter MAC\n", - dev->name); - goto err_out; - } - } - - /* Request a shared interrupt line. */ - error = request_irq (dev->irq, RCinterrupt, SA_SHIRQ, dev->name, dev); - if (error) { - printk (KERN_ERR "%s: unable to get IRQ %d\n", - dev->name, dev->irq); - goto err_out; - } - - DriverControlWord |= WARM_REBOOT_CAPABLE; - RCReportDriverCapability (dev, DriverControlWord); - - printk (KERN_INFO "%s: RedCreek Communications IPSEC VPN adapter\n", - dev->name); - - RCEnableI2OInterrupts (dev); - - while (post_buffers) { - if (post_buffers > MAX_NMBR_POST_BUFFERS_PER_MSG) - requested = MAX_NMBR_POST_BUFFERS_PER_MSG; - else - requested = post_buffers; - count = RC_allocate_and_post_buffers (dev, requested); - - if (count < requested) { - /* - * Check to see if we were able to post - * any buffers at all. - */ - if (post_buffers == MAX_NMBR_RCV_BUFFERS) { - printk (KERN_ERR "%s: \ - unable to allocate any buffers\n", - dev->name); - goto err_out_free_irq; - } - printk (KERN_WARNING "%s: \ - unable to allocate all requested buffers\n", dev->name); - break; /* we'll try to post more buffers later */ - } else - post_buffers -= count; - } - pDpa->numOutRcvBuffers = MAX_NMBR_RCV_BUFFERS - post_buffers; - pDpa->shutdown = 0; /* just in case */ - netif_start_queue (dev); - return 0; - -err_out_free_irq: - free_irq (dev->irq, dev); -err_out: - return error; -} - -static int -RC_xmit_packet (struct sk_buff *skb, struct net_device *dev) -{ - - PDPA pDpa = dev->priv; - singleTCB tcb; - psingleTCB ptcb = &tcb; - RC_RETURN status = 0; - - netif_stop_queue (dev); - - if (pDpa->shutdown || pDpa->reboot) { - printk ("RC_xmit_packet: tbusy!\n"); - return 1; - } - - /* - * The user is free to reuse the TCB after RCI2OSendPacket() - * returns, since the function copies the necessary info into its - * own private space. Thus, our TCB can be a local structure. - * The skb, on the other hand, will be freed up in our interrupt - * handler. - */ - - ptcb->bcount = 1; - - /* - * we'll get the context when the adapter interrupts us to tell us that - * the transmission is done. At that time, we can free skb. - */ - ptcb->b.context = (U32) skb; - ptcb->b.scount = 1; - ptcb->b.size = skb->len; - ptcb->b.addr = pci_map_single(pDpa->pci_dev, skb->data, skb->len, - PCI_DMA_TODEVICE); - - if ((status = RCI2OSendPacket (dev, (U32) NULL, (PRCTCB) ptcb)) - != RC_RTN_NO_ERROR) { - printk ("%s: send error 0x%x\n", dev->name, (uint) status); - return 1; - } else { - dev->trans_start = jiffies; - netif_wake_queue (dev); - } - /* - * That's it! - */ - return 0; -} - -/* - * RCxmit_callback() - * - * The transmit callback routine. It's called by RCProcI2OMsgQ() - * because the adapter is done with one or more transmit buffers and - * it's returning them to us, or we asked the adapter to return the - * outstanding transmit buffers by calling RCResetLANCard() with - * RC_RESOURCE_RETURN_PEND_TX_BUFFERS flag. - * All we need to do is free the buffers. - */ -static void -RCxmit_callback (U32 Status, - U16 PcktCount, PU32 BufferContext, struct net_device *dev) -{ - struct sk_buff *skb; - PDPA pDpa = dev->priv; - - if (!pDpa) { - printk (KERN_ERR "%s: Fatal Error in xmit callback, !pDpa\n", - dev->name); - return; - } - - if (Status != I2O_REPLY_STATUS_SUCCESS) - printk (KERN_INFO "%s: xmit_callback: Status = 0x%x\n", - dev->name, (uint) Status); - if (pDpa->shutdown || pDpa->reboot) - printk (KERN_INFO "%s: xmit callback: shutdown||reboot\n", - dev->name); - - while (PcktCount--) { - skb = (struct sk_buff *) (BufferContext[0]); - BufferContext++; - pci_unmap_single(pDpa->pci_dev, BufferContext[1], skb->len, - PCI_DMA_TODEVICE); - dev_kfree_skb_irq (skb); - } - netif_wake_queue (dev); -} - -static void -RCreset_callback (U32 Status, U32 p1, U32 p2, struct net_device *dev) -{ - PDPA pDpa = dev->priv; - - printk ("RCreset_callback Status 0x%x\n", (uint) Status); - /* - * Check to see why we were called. - */ - if (pDpa->shutdown) { - printk (KERN_INFO "%s: shutting down interface\n", - dev->name); - pDpa->shutdown = 0; - pDpa->reboot = 0; - } else if (pDpa->reboot) { - printk (KERN_INFO "%s: reboot, shutdown adapter\n", - dev->name); - /* - * We don't set any of the flags in RCShutdownLANCard() - * and we don't pass a callback routine to it. - * The adapter will have already initiated the reboot by - * the time the function returns. - */ - RCDisableI2OInterrupts (dev); - RCShutdownLANCard (dev, 0, 0, 0); - printk (KERN_INFO "%s: scheduling timer...\n", dev->name); - init_timer (&pDpa->timer); - pDpa->timer.expires = RUN_AT ((40 * HZ) / 10); /* 4 sec. */ - pDpa->timer.data = (unsigned long) dev; - pDpa->timer.function = &rc_timer; /* timer handler */ - add_timer (&pDpa->timer); - } -} - -static void -RCreboot_callback (U32 Status, U32 p1, U32 p2, struct net_device *dev) -{ - PDPA pDpa = dev->priv; - - printk (KERN_INFO "%s: reboot: rcv buffers outstanding = %d\n", - dev->name, (uint) pDpa->numOutRcvBuffers); - - if (pDpa->shutdown) { - printk (KERN_INFO "%s: skip reboot, shutdown initiated\n", - dev->name); - return; - } - pDpa->reboot = 1; - /* - * OK, we reset the adapter and ask it to return all - * outstanding transmit buffers as well as the posted - * receive buffers. When the adapter is done returning - * those buffers, it will call our RCreset_callback() - * routine. In that routine, we'll call RCShutdownLANCard() - * to tell the adapter that it's OK to start the reboot and - * schedule a timer callback routine to execute 3 seconds - * later; this routine will reinitialize the adapter at that time. - */ - RCResetLANCard (dev, RC_RESOURCE_RETURN_POSTED_RX_BUCKETS | - RC_RESOURCE_RETURN_PEND_TX_BUFFERS, 0, - (PFNCALLBACK) RCreset_callback); -} - -/* - * RCrecv_callback() - * - * The receive packet callback routine. This is called by - * RCProcI2OMsgQ() after the adapter posts buffers which have been - * filled (one ethernet packet per buffer). - */ -static void -RCrecv_callback (U32 Status, - U8 PktCount, - U32 BucketsRemain, - PU32 PacketDescBlock, struct net_device *dev) -{ - - U32 len, count; - PDPA pDpa = dev->priv; - struct sk_buff *skb; - singleTCB tcb; - psingleTCB ptcb = &tcb; - - ptcb->bcount = 1; - - if ((pDpa->shutdown || pDpa->reboot) && !Status) - printk (KERN_INFO "%s: shutdown||reboot && !Status (%d)\n", - dev->name, PktCount); - - if ((Status != I2O_REPLY_STATUS_SUCCESS) || pDpa->shutdown) { - /* - * Free whatever buffers the adapter returned, but don't - * pass them to the kernel. - */ - - if (!pDpa->shutdown && !pDpa->reboot) - printk (KERN_INFO "%s: recv error status = 0x%x\n", - dev->name, (uint) Status); - else - printk (KERN_DEBUG "%s: Returning %d buffs stat 0x%x\n", - dev->name, PktCount, (uint) Status); - /* - * TO DO: check the nature of the failure and put the - * adapter in failed mode if it's a hard failure. - * Send a reset to the adapter and free all outstanding memory. - */ - if (PacketDescBlock) { - while (PktCount--) { - skb = (struct sk_buff *) PacketDescBlock[0]; - dev_kfree_skb (skb); - pDpa->numOutRcvBuffers--; - /* point to next context field */ - PacketDescBlock += BD_SIZE; - } - } - return; - } else { - while (PktCount--) { - skb = (struct sk_buff *) PacketDescBlock[0]; - len = PacketDescBlock[2]; - skb->dev = dev; - skb_put (skb, len); /* adjust length and tail */ - skb->protocol = eth_type_trans (skb, dev); - netif_rx (skb); /* send the packet to the kernel */ - dev->last_rx = jiffies; - pDpa->numOutRcvBuffers--; - /* point to next context field */ - PacketDescBlock += BD_SIZE; - } - } - - /* - * Replenish the posted receive buffers. - * DO NOT replenish buffers if the driver has already - * initiated a reboot or shutdown! - */ - - if (!pDpa->shutdown && !pDpa->reboot) { - count = RC_allocate_and_post_buffers (dev, - MAX_NMBR_RCV_BUFFERS - - pDpa->numOutRcvBuffers); - pDpa->numOutRcvBuffers += count; - } - -} - -/* - * RCinterrupt() - * - * Interrupt handler. - * This routine sets up a couple of pointers and calls - * RCProcI2OMsgQ(), which in turn process the message and - * calls one of our callback functions. - */ -static irqreturn_t -RCinterrupt (int irq, void *dev_id, struct pt_regs *regs) -{ - - PDPA pDpa; - struct net_device *dev = dev_id; - - pDpa = dev->priv; - - if (pDpa->shutdown) - printk (KERN_DEBUG "%s: shutdown, service irq\n", - dev->name); - - return RCProcI2OMsgQ (dev); -} - -#define REBOOT_REINIT_RETRY_LIMIT 4 -static void -rc_timer (unsigned long data) -{ - struct net_device *dev = (struct net_device *) data; - PDPA pDpa = dev->priv; - int init_status; - static int retry; - int post_buffers = MAX_NMBR_RCV_BUFFERS; - int count = 0; - int requested = 0; - - if (pDpa->reboot) { - init_status = - RCInitI2OMsgLayer (dev, (PFNTXCALLBACK) RCxmit_callback, - (PFNRXCALLBACK) RCrecv_callback, - (PFNCALLBACK) RCreboot_callback); - - switch (init_status) { - case RC_RTN_NO_ERROR: - - pDpa->reboot = 0; - pDpa->shutdown = 0; /* just in case */ - RCReportDriverCapability (dev, DriverControlWord); - RCEnableI2OInterrupts (dev); - - - if (!(dev->flags & IFF_UP)) { - retry = 0; - return; - } - while (post_buffers) { - if (post_buffers > - MAX_NMBR_POST_BUFFERS_PER_MSG) - requested = - MAX_NMBR_POST_BUFFERS_PER_MSG; - else - requested = post_buffers; - count = - RC_allocate_and_post_buffers (dev, - requested); - post_buffers -= count; - if (count < requested) - break; - } - pDpa->numOutRcvBuffers = - MAX_NMBR_RCV_BUFFERS - post_buffers; - printk ("Initialization done.\n"); - netif_wake_queue (dev); - retry = 0; - return; - case RC_RTN_FREE_Q_EMPTY: - retry++; - printk (KERN_WARNING "%s inbound free q empty\n", - dev->name); - break; - default: - retry++; - printk (KERN_WARNING "%s bad stat after reboot: %d\n", - dev->name, init_status); - break; - } - - if (retry > REBOOT_REINIT_RETRY_LIMIT) { - printk (KERN_WARNING "%s unable to reinitialize adapter after reboot\n", dev->name); - printk (KERN_WARNING "%s shutting down interface\n", dev->name); - RCDisableI2OInterrupts (dev); - dev->flags &= ~IFF_UP; - } else { - printk (KERN_INFO "%s: rescheduling timer...\n", - dev->name); - init_timer (&pDpa->timer); - pDpa->timer.expires = RUN_AT ((40 * HZ) / 10); - pDpa->timer.data = (unsigned long) dev; - pDpa->timer.function = &rc_timer; - add_timer (&pDpa->timer); - } - } else - printk (KERN_WARNING "%s: unexpected timer irq\n", dev->name); -} - -static int -RCclose (struct net_device *dev) -{ - PDPA pDpa = dev->priv; - - printk("RCclose\n"); - netif_stop_queue (dev); - - if (pDpa->reboot) { - printk (KERN_INFO "%s skipping reset -- adapter already in reboot mode\n", dev->name); - dev->flags &= ~IFF_UP; - pDpa->shutdown = 1; - return 0; - } - - pDpa->shutdown = 1; - - /* - * We can't allow the driver to be unloaded until the adapter returns - * all posted receive buffers. It doesn't hurt to tell the adapter - * to return all posted receive buffers and outstanding xmit buffers, - * even if there are none. - */ - - RCShutdownLANCard (dev, RC_RESOURCE_RETURN_POSTED_RX_BUCKETS | - RC_RESOURCE_RETURN_PEND_TX_BUFFERS, 0, - (PFNCALLBACK) RCreset_callback); - - dev->flags &= ~IFF_UP; - return 0; -} - -static struct net_device_stats * -RCget_stats (struct net_device *dev) -{ - RCLINKSTATS RCstats; - - PDPA pDpa = dev->priv; - - if (!pDpa) { - return 0; - } else if (!(dev->flags & IFF_UP)) { - return 0; - } - - memset (&RCstats, 0, sizeof (RCLINKSTATS)); - if ((RCGetLinkStatistics (dev, &RCstats, (void *) 0)) == - RC_RTN_NO_ERROR) { - - /* total packets received */ - pDpa->stats.rx_packets = RCstats.Rcv_good - /* total packets transmitted */; - pDpa->stats.tx_packets = RCstats.TX_good; - - pDpa->stats.rx_errors = RCstats.Rcv_CRCerr + - RCstats.Rcv_alignerr + RCstats.Rcv_reserr + - RCstats.Rcv_orun + RCstats.Rcv_cdt + RCstats.Rcv_runt; - - pDpa->stats.tx_errors = RCstats.TX_urun + RCstats.TX_crs + - RCstats.TX_def + RCstats.TX_totcol; - - /* - * This needs improvement. - */ - pDpa->stats.rx_dropped = 0; /* no space in linux buffers */ - pDpa->stats.tx_dropped = 0; /* no space available in linux */ - pDpa->stats.multicast = 0; /* multicast packets received */ - pDpa->stats.collisions = RCstats.TX_totcol; - - /* detailed rx_errors: */ - pDpa->stats.rx_length_errors = 0; - pDpa->stats.rx_over_errors = RCstats.Rcv_orun; - pDpa->stats.rx_crc_errors = RCstats.Rcv_CRCerr; - pDpa->stats.rx_frame_errors = 0; - pDpa->stats.rx_fifo_errors = 0; - pDpa->stats.rx_missed_errors = 0; - - /* detailed tx_errors */ - pDpa->stats.tx_aborted_errors = 0; - pDpa->stats.tx_carrier_errors = 0; - pDpa->stats.tx_fifo_errors = 0; - pDpa->stats.tx_heartbeat_errors = 0; - pDpa->stats.tx_window_errors = 0; - - return ((struct net_device_stats *) &(pDpa->stats)); - } - return 0; -} - -static int -RCioctl (struct net_device *dev, struct ifreq *rq, int cmd) -{ - RCuser_struct RCuser; - PDPA pDpa = dev->priv; - - if (!capable (CAP_NET_ADMIN)) - return -EPERM; - - switch (cmd) { - - case RCU_PROTOCOL_REV: - /* - * Assign user protocol revision, to tell user-level - * controller program whether or not it's in sync. - */ - rq->ifr_ifru.ifru_data = (caddr_t) USER_PROTOCOL_REV; - break; - - case RCU_COMMAND: - { - if (copy_from_user - (&RCuser, rq->ifr_data, sizeof (RCuser))) - return -EFAULT; - - dprintk ("RCioctl: RCuser_cmd = 0x%x\n", RCuser.cmd); - - switch (RCuser.cmd) { - case RCUC_GETFWVER: - RCUD_GETFWVER = &RCuser.RCUS_GETFWVER; - RCGetFirmwareVer (dev, - (PU8) & RCUD_GETFWVER-> - FirmString, NULL); - break; - case RCUC_GETINFO: - RCUD_GETINFO = &RCuser.RCUS_GETINFO; - RCUD_GETINFO->mem_start = dev->base_addr; - RCUD_GETINFO->mem_end = - dev->base_addr + pDpa->pci_addr_len; - RCUD_GETINFO->base_addr = pDpa->pci_addr; - RCUD_GETINFO->irq = dev->irq; - break; - case RCUC_GETIPANDMASK: - RCUD_GETIPANDMASK = &RCuser.RCUS_GETIPANDMASK; - RCGetRavlinIPandMask (dev, - (PU32) & - RCUD_GETIPANDMASK->IpAddr, - (PU32) & - RCUD_GETIPANDMASK-> - NetMask, NULL); - break; - case RCUC_GETLINKSTATISTICS: - RCUD_GETLINKSTATISTICS = - &RCuser.RCUS_GETLINKSTATISTICS; - RCGetLinkStatistics (dev, - (P_RCLINKSTATS) & - RCUD_GETLINKSTATISTICS-> - StatsReturn, NULL); - break; - case RCUC_GETLINKSTATUS: - RCUD_GETLINKSTATUS = &RCuser.RCUS_GETLINKSTATUS; - RCGetLinkStatus (dev, - (PU32) & RCUD_GETLINKSTATUS-> - ReturnStatus, NULL); - break; - case RCUC_GETMAC: - RCUD_GETMAC = &RCuser.RCUS_GETMAC; - RCGetMAC (dev, NULL); - memcpy(RCUD_GETMAC, dev->dev_addr, 8); - break; - case RCUC_GETPROM: - RCUD_GETPROM = &RCuser.RCUS_GETPROM; - RCGetPromiscuousMode (dev, - (PU32) & RCUD_GETPROM-> - PromMode, NULL); - break; - case RCUC_GETBROADCAST: - RCUD_GETBROADCAST = &RCuser.RCUS_GETBROADCAST; - RCGetBroadcastMode (dev, - (PU32) & RCUD_GETBROADCAST-> - BroadcastMode, NULL); - break; - case RCUC_GETSPEED: - if (!(dev->flags & IFF_UP)) { - return -ENODATA; - } - RCUD_GETSPEED = &RCuser.RCUS_GETSPEED; - RCGetLinkSpeed (dev, - (PU32) & RCUD_GETSPEED-> - LinkSpeedCode, NULL); - break; - case RCUC_SETIPANDMASK: - RCUD_SETIPANDMASK = &RCuser.RCUS_SETIPANDMASK; - RCSetRavlinIPandMask (dev, - (U32) RCUD_SETIPANDMASK-> - IpAddr, - (U32) RCUD_SETIPANDMASK-> - NetMask); - break; - case RCUC_SETMAC: - RCSetMAC (dev, (PU8) & RCUD_SETMAC->mac); - break; - case RCUC_SETSPEED: - RCUD_SETSPEED = &RCuser.RCUS_SETSPEED; - RCSetLinkSpeed (dev, - (U16) RCUD_SETSPEED-> - LinkSpeedCode); - break; - case RCUC_SETPROM: - RCUD_SETPROM = &RCuser.RCUS_SETPROM; - RCSetPromiscuousMode (dev, - (U16) RCUD_SETPROM-> - PromMode); - break; - case RCUC_SETBROADCAST: - RCUD_SETBROADCAST = &RCuser.RCUS_SETBROADCAST; - RCSetBroadcastMode (dev, - (U16) RCUD_SETBROADCAST-> - BroadcastMode); - break; - default: - RCUD_DEFAULT = &RCuser.RCUS_DEFAULT; - RCUD_DEFAULT->rc = 0x11223344; - break; - } - if (copy_to_user (rq->ifr_data, &RCuser, - sizeof (RCuser))) - return -EFAULT; - break; - } /* RCU_COMMAND */ - - default: - rq->ifr_ifru.ifru_data = (caddr_t) 0x12345678; - return -EINVAL; - } - return 0; -} - -static int -RCconfig (struct net_device *dev, struct ifmap *map) -{ - /* - * To be completed ... - */ - return 0; - if (dev->flags & IFF_UP) /* can't act on a running interface */ - return -EBUSY; - - /* Don't allow changing the I/O address */ - if (map->base_addr != dev->base_addr) { - printk (KERN_WARNING "%s Change I/O address not implemented\n", - dev->name); - return -EOPNOTSUPP; - } - return 0; -} - -static void __exit -rcpci_cleanup_module (void) -{ - pci_unregister_driver (&rcpci45_driver); -} - -module_init (rcpci_init_module); -module_exit (rcpci_cleanup_module); - -static int -RC_allocate_and_post_buffers (struct net_device *dev, int numBuffers) -{ - - int i; - PU32 p; - psingleB pB; - struct sk_buff *skb; - PDPA pDpa = dev->priv; - RC_RETURN status; - U32 res = 0; - - if (!numBuffers) - return 0; - else if (numBuffers > MAX_NMBR_POST_BUFFERS_PER_MSG) { - printk (KERN_ERR "%s: Too many buffers requested!\n", - dev->name); - numBuffers = 32; - } - - p = (PU32) kmalloc (sizeof (U32) + numBuffers * sizeof (singleB), - GFP_DMA | GFP_ATOMIC); - - if (!p) { - printk (KERN_WARNING "%s unable to allocate TCB\n", - dev->name); - goto out; - } - - p[0] = 0; /* Buffer Count */ - pB = (psingleB) ((U32) p + sizeof (U32));/* point to the first buffer */ - - for (i = 0; i < numBuffers; i++) { - skb = dev_alloc_skb (MAX_ETHER_SIZE + 2); - if (!skb) { - printk (KERN_WARNING - "%s: unable to allocate enough skbs!\n", - dev->name); - goto err_out_unmap; - } - skb_reserve (skb, 2); /* Align IP on 16 byte boundaries */ - pB->context = (U32) skb; - pB->scount = 1; /* segment count */ - pB->size = MAX_ETHER_SIZE; - pB->addr = pci_map_single(pDpa->pci_dev, skb->data, - MAX_ETHER_SIZE, PCI_DMA_FROMDEVICE); - p[0]++; - pB++; - } - - if ((status = RCPostRecvBuffers (dev, (PRCTCB) p)) != RC_RTN_NO_ERROR) { - printk (KERN_WARNING "%s: Post buffer failed, error 0x%x\n", - dev->name, status); - goto err_out_unmap; - } -out_free: - res = p[0]; - kfree (p); -out: - return (res); /* return the number of posted buffers */ - -err_out_unmap: - for (; p[0] > 0; p[0]--) { - --pB; - skb = (struct sk_buff *) pB->context; - pci_unmap_single(pDpa->pci_dev, pB->addr, MAX_ETHER_SIZE, - PCI_DMA_FROMDEVICE); - dev_kfree_skb (skb); - } - goto out_free; -} diff --git a/drivers/net/wan/comx-hw-comx.c b/drivers/net/wan/comx-hw-comx.c deleted file mode 100644 index a62fe5514..000000000 --- a/drivers/net/wan/comx-hw-comx.c +++ /dev/null @@ -1,1450 +0,0 @@ -/* - * Hardware-level driver for the COMX and HICOMX cards - * for Linux kernel 2.2.X - * - * Original authors: Arpad Bakay , - * Peter Bajan , - * Rewritten by: Tivadar Szemethy - * Currently maintained by: Gergely Madarasz - * - * Copyright (C) 1995-2000 ITConsult-Pro Co. - * - * Contributors: - * Arnaldo Carvalho de Melo - 0.86 - * Daniele Bellucci - 0.87 - * - * 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. - * - * Version 0.80 (99/06/11): - * - port back to kernel, add support builtin driver - * - cleaned up the source code a bit - * - * Version 0.81 (99/06/22): - * - cleaned up the board load functions, no more long reset - * timeouts - * - lower modem lines on close - * - some interrupt handling fixes - * - * Version 0.82 (99/08/24): - * - fix multiple board support - * - * Version 0.83 (99/11/30): - * - interrupt handling and locking fixes during initalization - * - really fix multiple board support - * - * Version 0.84 (99/12/02): - * - some workarounds for problematic hardware/firmware - * - * Version 0.85 (00/01/14): - * - some additional workarounds :/ - * - printk cleanups - * Version 0.86 (00/08/15): - * - resource release on failure at COMX_init - * - * Version 0.87 (03/07/09) - * - audit copy_from_user in comxhw_write_proc - */ - -#define VERSION "0.87" - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "comx.h" -#include "comxhw.h" - -MODULE_AUTHOR("Gergely Madarasz , Tivadar Szemethy , Arpad Bakay"); -MODULE_DESCRIPTION("Hardware-level driver for the COMX and HICOMX adapters\n"); -MODULE_LICENSE("GPL"); - -#define COMX_readw(dev, offset) (readw(dev->mem_start + offset + \ - (unsigned int)(((struct comx_privdata *)\ - ((struct comx_channel *)dev->priv)->HW_privdata)->channel) \ - * COMX_CHANNEL_OFFSET)) - -#define COMX_WRITE(dev, offset, value) (writew(value, dev->mem_start + offset \ - + (unsigned int)(((struct comx_privdata *) \ - ((struct comx_channel *)dev->priv)->HW_privdata)->channel) \ - * COMX_CHANNEL_OFFSET)) - -#define COMX_CMD(dev, cmd) (COMX_WRITE(dev, OFF_A_L2_CMD, cmd)) - -struct comx_firmware { - int len; - unsigned char *data; -}; - -struct comx_privdata { - struct comx_firmware *firmware; - u16 clock; - char channel; // channel no. - int memory_size; - short io_extent; - u_long histogram[5]; -}; - -static struct net_device *memory_used[(COMX_MEM_MAX - COMX_MEM_MIN) / 0x10000]; -extern struct comx_hardware hicomx_hw; -extern struct comx_hardware comx_hw; -extern struct comx_hardware cmx_hw; - -static irqreturn_t COMX_interrupt(int irq, void *dev_id, struct pt_regs *regs); - -static void COMX_board_on(struct net_device *dev) -{ - outb_p( (byte) (((dev->mem_start & 0xf0000) >> 16) | - COMX_ENABLE_BOARD_IT | COMX_ENABLE_BOARD_MEM), dev->base_addr); -} - -static void COMX_board_off(struct net_device *dev) -{ - outb_p( (byte) (((dev->mem_start & 0xf0000) >> 16) | - COMX_ENABLE_BOARD_IT), dev->base_addr); -} - -static void HICOMX_board_on(struct net_device *dev) -{ - outb_p( (byte) (((dev->mem_start & 0xf0000) >> 12) | - HICOMX_ENABLE_BOARD_MEM), dev->base_addr); -} - -static void HICOMX_board_off(struct net_device *dev) -{ - outb_p( (byte) (((dev->mem_start & 0xf0000) >> 12) | - HICOMX_DISABLE_BOARD_MEM), dev->base_addr); -} - -static void COMX_set_clock(struct net_device *dev) -{ - struct comx_channel *ch = dev->priv; - struct comx_privdata *hw = ch->HW_privdata; - - COMX_WRITE(dev, OFF_A_L1_CLKINI, hw->clock); -} - -static struct net_device *COMX_access_board(struct net_device *dev) -{ - struct comx_channel *ch = dev->priv; - struct net_device *ret; - int mempos = (dev->mem_start - COMX_MEM_MIN) >> 16; - unsigned long flags; - - - save_flags(flags); cli(); - - ret = memory_used[mempos]; - - if(ret == dev) { - goto out; - } - - memory_used[mempos] = dev; - - if (!ch->twin || ret != ch->twin) { - if (ret) ((struct comx_channel *)ret->priv)->HW_board_off(ret); - ch->HW_board_on(dev); - } -out: - restore_flags(flags); - return ret; -} - -static void COMX_release_board(struct net_device *dev, struct net_device *savep) -{ - unsigned long flags; - int mempos = (dev->mem_start - COMX_MEM_MIN) >> 16; - struct comx_channel *ch = dev->priv; - - save_flags(flags); cli(); - - if (memory_used[mempos] == savep) { - goto out; - } - - memory_used[mempos] = savep; - if (!ch->twin || ch->twin != savep) { - ch->HW_board_off(dev); - if (savep) ((struct comx_channel*)savep->priv)->HW_board_on(savep); - } -out: - restore_flags(flags); -} - -static int COMX_txe(struct net_device *dev) -{ - struct net_device *savep; - struct comx_channel *ch = dev->priv; - int rc = 0; - - savep = ch->HW_access_board(dev); - if (COMX_readw(dev,OFF_A_L2_LINKUP) == LINKUP_READY) { - rc = COMX_readw(dev,OFF_A_L2_TxEMPTY); - } - ch->HW_release_board(dev,savep); - if(rc==0xffff) { - printk(KERN_ERR "%s, OFF_A_L2_TxEMPTY is %d\n",dev->name, rc); - } - return rc; -} - -static int COMX_send_packet(struct net_device *dev, struct sk_buff *skb) -{ - struct net_device *savep; - struct comx_channel *ch = dev->priv; - struct comx_privdata *hw = ch->HW_privdata; - int ret = FRAME_DROPPED; - word tmp; - - savep = ch->HW_access_board(dev); - - if (ch->debug_flags & DEBUG_HW_TX) { - comx_debug_bytes(dev, skb->data, skb->len,"COMX_send packet"); - } - - if (skb->len > COMX_MAX_TX_SIZE) { - ret=FRAME_DROPPED; - goto out; - } - - tmp=COMX_readw(dev, OFF_A_L2_TxEMPTY); - if ((ch->line_status & LINE_UP) && tmp==1) { - int lensave = skb->len; - int dest = COMX_readw(dev, OFF_A_L2_TxBUFP); - word *data = (word *)skb->data; - - if(dest==0xffff) { - printk(KERN_ERR "%s: OFF_A_L2_TxBUFP is %d\n", dev->name, dest); - ret=FRAME_DROPPED; - goto out; - } - - writew((unsigned short)skb->len, dev->mem_start + dest); - dest += 2; - while (skb->len > 1) { - writew(*data++, dev->mem_start + dest); - dest += 2; skb->len -= 2; - } - if (skb->len == 1) { - writew(*((byte *)data), dev->mem_start + dest); - } - writew(0, dev->mem_start + (int)hw->channel * - COMX_CHANNEL_OFFSET + OFF_A_L2_TxEMPTY); - ch->stats.tx_packets++; - ch->stats.tx_bytes += lensave; - ret = FRAME_ACCEPTED; - } else { - ch->stats.tx_dropped++; - printk(KERN_INFO "%s: frame dropped\n",dev->name); - if(tmp) { - printk(KERN_ERR "%s: OFF_A_L2_TxEMPTY is %d\n",dev->name,tmp); - } - } - -out: - ch->HW_release_board(dev, savep); - dev_kfree_skb(skb); - return ret; -} - -static inline int comx_read_buffer(struct net_device *dev) -{ - struct comx_channel *ch = dev->priv; - word rbuf_offs; - struct sk_buff *skb; - word len; - int i=0; - word *writeptr; - - i = 0; - rbuf_offs = COMX_readw(dev, OFF_A_L2_RxBUFP); - if(rbuf_offs == 0xffff) { - printk(KERN_ERR "%s: OFF_A_L2_RxBUFP is %d\n",dev->name,rbuf_offs); - return 0; - } - len = readw(dev->mem_start + rbuf_offs); - if(len > COMX_MAX_RX_SIZE) { - printk(KERN_ERR "%s: packet length is %d\n",dev->name,len); - return 0; - } - if ((skb = dev_alloc_skb(len + 16)) == NULL) { - ch->stats.rx_dropped++; - COMX_WRITE(dev, OFF_A_L2_DAV, 0); - return 0; - } - rbuf_offs += 2; - skb_reserve(skb, 16); - skb_put(skb, len); - skb->dev = dev; - writeptr = (word *)skb->data; - while (i < len) { - *writeptr++ = readw(dev->mem_start + rbuf_offs); - rbuf_offs += 2; - i += 2; - } - COMX_WRITE(dev, OFF_A_L2_DAV, 0); - ch->stats.rx_packets++; - ch->stats.rx_bytes += len; - if (ch->debug_flags & DEBUG_HW_RX) { - comx_debug_skb(dev, skb, "COMX_interrupt receiving"); - } - ch->LINE_rx(dev, skb); - return 1; -} - -static inline char comx_line_change(struct net_device *dev, char linestat) -{ - struct comx_channel *ch=dev->priv; - char idle=1; - - - if (linestat & LINE_UP) { /* Vonal fol */ - if (ch->lineup_delay) { - if (!test_and_set_bit(0, &ch->lineup_pending)) { - ch->lineup_timer.function = comx_lineup_func; - ch->lineup_timer.data = (unsigned long)dev; - ch->lineup_timer.expires = jiffies + - HZ*ch->lineup_delay; - add_timer(&ch->lineup_timer); - idle=0; - } - } else { - idle=0; - ch->LINE_status(dev, ch->line_status |= LINE_UP); - } - } else { /* Vonal le */ - idle=0; - if (test_and_clear_bit(0, &ch->lineup_pending)) { - del_timer(&ch->lineup_timer); - } else { - ch->line_status &= ~LINE_UP; - if (ch->LINE_status) { - ch->LINE_status(dev, ch->line_status); - } - } - } - return idle; -} - - - -static irqreturn_t COMX_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - struct net_device *dev = dev_id; - struct comx_channel *ch = dev->priv; - struct comx_privdata *hw = ch->HW_privdata; - struct net_device *interrupted; - unsigned long jiffs; - char idle = 0; - int count = 0; - word tmp; - - if (dev == NULL) { - printk(KERN_ERR "COMX_interrupt: irq %d for unknown device\n", irq); - return IRQ_NONE; - } - - jiffs = jiffies; - - interrupted = ch->HW_access_board(dev); - - while (!idle && count < 5000) { - char channel = 0; - idle = 1; - - while (channel < 2) { - char linestat = 0; - char buffers_emptied = 0; - - if (channel == 1) { - if (ch->twin) { - dev = ch->twin; - ch = dev->priv; - hw = ch->HW_privdata; - } else { - break; - } - } else { - COMX_WRITE(dev, OFF_A_L1_REPENA, - COMX_readw(dev, OFF_A_L1_REPENA) & 0xFF00); - } - channel++; - - if ((ch->init_status & (HW_OPEN | LINE_OPEN)) != - (HW_OPEN | LINE_OPEN)) { - continue; - } - - /* Collect stats */ - tmp = COMX_readw(dev, OFF_A_L1_ABOREC); - COMX_WRITE(dev, OFF_A_L1_ABOREC, 0); - if(tmp==0xffff) { - printk(KERN_ERR "%s: OFF_A_L1_ABOREC is %d\n",dev->name,tmp); - break; - } else { - ch->stats.rx_missed_errors += (tmp >> 8) & 0xff; - ch->stats.rx_over_errors += tmp & 0xff; - } - tmp = COMX_readw(dev, OFF_A_L1_CRCREC); - COMX_WRITE(dev, OFF_A_L1_CRCREC, 0); - if(tmp==0xffff) { - printk(KERN_ERR "%s: OFF_A_L1_CRCREC is %d\n",dev->name,tmp); - break; - } else { - ch->stats.rx_crc_errors += (tmp >> 8) & 0xff; - ch->stats.rx_missed_errors += tmp & 0xff; - } - - if ((ch->line_status & LINE_UP) && ch->LINE_rx) { - tmp=COMX_readw(dev, OFF_A_L2_DAV); - while (tmp==1) { - idle=0; - buffers_emptied+=comx_read_buffer(dev); - tmp=COMX_readw(dev, OFF_A_L2_DAV); - } - if(tmp) { - printk(KERN_ERR "%s: OFF_A_L2_DAV is %d\n", dev->name, tmp); - break; - } - } - - tmp=COMX_readw(dev, OFF_A_L2_TxEMPTY); - if (tmp==1 && ch->LINE_tx) { - ch->LINE_tx(dev); - } - if(tmp==0xffff) { - printk(KERN_ERR "%s: OFF_A_L2_TxEMPTY is %d\n", dev->name, tmp); - break; - } - - if (COMX_readw(dev, OFF_A_L1_PBUFOVR) >> 8) { - linestat &= ~LINE_UP; - } else { - linestat |= LINE_UP; - } - - if ((linestat & LINE_UP) != (ch->line_status & LINE_UP)) { - ch->stats.tx_carrier_errors++; - idle &= comx_line_change(dev,linestat); - } - - hw->histogram[(int)buffers_emptied]++; - } - count++; - } - - if(count==5000) { - printk(KERN_WARNING "%s: interrupt stuck\n",dev->name); - } - - ch->HW_release_board(dev, interrupted); - return IRQ_HANDLED; -} - -static int COMX_open(struct net_device *dev) -{ - struct comx_channel *ch = dev->priv; - struct comx_privdata *hw = ch->HW_privdata; - struct proc_dir_entry *procfile = ch->procdir->subdir; - unsigned long jiffs; - int twin_open=0; - int retval; - struct net_device *savep; - - if (!dev->base_addr || !dev->irq || !dev->mem_start) { - return -ENODEV; - } - - if (ch->twin && (((struct comx_channel *)(ch->twin->priv))->init_status & HW_OPEN)) { - twin_open=1; - } - - if (!twin_open) { - if (!request_region(dev->base_addr, hw->io_extent, dev->name)) { - return -EAGAIN; - } - if (request_irq(dev->irq, COMX_interrupt, 0, dev->name, - (void *)dev)) { - printk(KERN_ERR "comx-hw-comx: unable to obtain irq %d\n", dev->irq); - release_region(dev->base_addr, hw->io_extent); - return -EAGAIN; - } - ch->init_status |= IRQ_ALLOCATED; - if (!ch->HW_load_board || ch->HW_load_board(dev)) { - ch->init_status &= ~IRQ_ALLOCATED; - retval=-ENODEV; - goto error; - } - } - - savep = ch->HW_access_board(dev); - COMX_WRITE(dev, OFF_A_L2_LINKUP, 0); - - if (ch->HW_set_clock) { - ch->HW_set_clock(dev); - } - - COMX_CMD(dev, COMX_CMD_INIT); - jiffs = jiffies; - while (COMX_readw(dev, OFF_A_L2_LINKUP) != 1 && time_before(jiffies, jiffs + HZ)) { - schedule_timeout(1); - } - - if (time_after_eq(jiffies, jiffs + HZ)) { - printk(KERN_ERR "%s: board timeout on INIT command\n", dev->name); - ch->HW_release_board(dev, savep); - retval=-EIO; - goto error; - } - udelay(1000); - - COMX_CMD(dev, COMX_CMD_OPEN); - - jiffs = jiffies; - while (COMX_readw(dev, OFF_A_L2_LINKUP) != 3 && time_before(jiffies, jiffs + HZ)) { - schedule_timeout(1); - } - - if (time_after_eq(jiffies, jiffs + HZ)) { - printk(KERN_ERR "%s: board timeout on OPEN command\n", dev->name); - ch->HW_release_board(dev, savep); - retval=-EIO; - goto error; - } - - ch->init_status |= HW_OPEN; - - /* Ez eleg ciki, de ilyen a rendszer */ - if (COMX_readw(dev, OFF_A_L1_PBUFOVR) >> 8) { - ch->line_status &= ~LINE_UP; - } else { - ch->line_status |= LINE_UP; - } - - if (ch->LINE_status) { - ch->LINE_status(dev, ch->line_status); - } - - ch->HW_release_board(dev, savep); - - for ( ; procfile ; procfile = procfile->next) { - if (strcmp(procfile->name, FILENAME_IRQ) == 0 - || strcmp(procfile->name, FILENAME_IO) == 0 - || strcmp(procfile->name, FILENAME_MEMADDR) == 0 - || strcmp(procfile->name, FILENAME_CHANNEL) == 0 - || strcmp(procfile->name, FILENAME_FIRMWARE) == 0 - || strcmp(procfile->name, FILENAME_CLOCK) == 0) { - procfile->mode = S_IFREG | 0444; - - } - } - - return 0; - -error: - if(!twin_open) { - release_region(dev->base_addr, hw->io_extent); - free_irq(dev->irq, (void *)dev); - } - return retval; - -} - -static int COMX_close(struct net_device *dev) -{ - struct comx_channel *ch = dev->priv; - struct proc_dir_entry *procfile = ch->procdir->subdir; - struct comx_privdata *hw = ch->HW_privdata; - struct comx_channel *twin_ch; - struct net_device *savep; - - savep = ch->HW_access_board(dev); - - COMX_CMD(dev, COMX_CMD_CLOSE); - udelay(1000); - COMX_CMD(dev, COMX_CMD_EXIT); - - ch->HW_release_board(dev, savep); - - if (ch->init_status & IRQ_ALLOCATED) { - free_irq(dev->irq, (void *)dev); - ch->init_status &= ~IRQ_ALLOCATED; - } - release_region(dev->base_addr, hw->io_extent); - - if (ch->twin && (twin_ch = ch->twin->priv) && - (twin_ch->init_status & HW_OPEN)) { - /* Pass the irq to the twin */ - if (request_irq(dev->irq, COMX_interrupt, 0, ch->twin->name, - (void *)ch->twin) == 0) { - twin_ch->init_status |= IRQ_ALLOCATED; - } - } - - for ( ; procfile ; procfile = procfile->next) { - if (strcmp(procfile->name, FILENAME_IRQ) == 0 - || strcmp(procfile->name, FILENAME_IO) == 0 - || strcmp(procfile->name, FILENAME_MEMADDR) == 0 - || strcmp(procfile->name, FILENAME_CHANNEL) == 0 - || strcmp(procfile->name, FILENAME_FIRMWARE) == 0 - || strcmp(procfile->name, FILENAME_CLOCK) == 0) { - procfile->mode = S_IFREG | 0644; - } - } - - ch->init_status &= ~HW_OPEN; - return 0; -} - -static int COMX_statistics(struct net_device *dev, char *page) -{ - struct comx_channel *ch = dev->priv; - struct comx_privdata *hw = ch->HW_privdata; - struct net_device *savep; - int len = 0; - - savep = ch->HW_access_board(dev); - - len += sprintf(page + len, "Board data: %s %s %s %s\nPBUFOVR: %02x, " - "MODSTAT: %02x, LINKUP: %02x, DAV: %02x\nRxBUFP: %02x, " - "TxEMPTY: %02x, TxBUFP: %02x\n", - (ch->init_status & HW_OPEN) ? "HW_OPEN" : "", - (ch->init_status & LINE_OPEN) ? "LINE_OPEN" : "", - (ch->init_status & FW_LOADED) ? "FW_LOADED" : "", - (ch->init_status & IRQ_ALLOCATED) ? "IRQ_ALLOCATED" : "", - COMX_readw(dev, OFF_A_L1_PBUFOVR) & 0xff, - (COMX_readw(dev, OFF_A_L1_PBUFOVR) >> 8) & 0xff, - COMX_readw(dev, OFF_A_L2_LINKUP) & 0xff, - COMX_readw(dev, OFF_A_L2_DAV) & 0xff, - COMX_readw(dev, OFF_A_L2_RxBUFP) & 0xff, - COMX_readw(dev, OFF_A_L2_TxEMPTY) & 0xff, - COMX_readw(dev, OFF_A_L2_TxBUFP) & 0xff); - - len += sprintf(page + len, "hist[0]: %8lu hist[1]: %8lu hist[2]: %8lu\n" - "hist[3]: %8lu hist[4]: %8lu\n",hw->histogram[0],hw->histogram[1], - hw->histogram[2],hw->histogram[3],hw->histogram[4]); - - ch->HW_release_board(dev, savep); - - return len; -} - -static int COMX_load_board(struct net_device *dev) -{ - struct comx_channel *ch = dev->priv; - struct comx_privdata *hw = ch->HW_privdata; - struct comx_firmware *fw = hw->firmware; - word board_segment = dev->mem_start >> 16; - int mempos = (dev->mem_start - COMX_MEM_MIN) >> 16; - unsigned long flags; - unsigned char id1, id2; - struct net_device *saved; - int retval; - int loopcount; - int len; - byte *COMX_address; - - if (!fw || !fw->len) { - struct comx_channel *twin_ch = ch->twin ? ch->twin->priv : NULL; - struct comx_privdata *twin_hw; - - if (!twin_ch || !(twin_hw = twin_ch->HW_privdata)) { - return -EAGAIN; - } - - if (!(fw = twin_hw->firmware) || !fw->len) { - return -EAGAIN; - } - } - - id1 = fw->data[OFF_FW_L1_ID]; - id2 = fw->data[OFF_FW_L1_ID + 1]; - - if (id1 != FW_L1_ID_1 || id2 != FW_L1_ID_2_COMX) { - printk(KERN_ERR "%s: incorrect firmware, load aborted\n", - dev->name); - return -EAGAIN; - } - - printk(KERN_INFO "%s: Loading COMX Layer 1 firmware %s\n", dev->name, - (char *)(fw->data + OFF_FW_L1_ID + 2)); - - id1 = fw->data[OFF_FW_L2_ID]; - id2 = fw->data[OFF_FW_L2_ID + 1]; - if (id1 == FW_L2_ID_1 && (id2 == 0xc0 || id2 == 0xc1 || id2 == 0xc2)) { - printk(KERN_INFO "with Layer 2 code %s\n", - (char *)(fw->data + OFF_FW_L2_ID + 2)); - } - - outb_p(board_segment | COMX_BOARD_RESET, dev->base_addr); - /* 10 usec should be enough here */ - udelay(100); - - save_flags(flags); cli(); - saved=memory_used[mempos]; - if(saved) { - ((struct comx_channel *)saved->priv)->HW_board_off(saved); - } - memory_used[mempos]=dev; - - outb_p(board_segment | COMX_ENABLE_BOARD_MEM, dev->base_addr); - - writeb(0, dev->mem_start + COMX_JAIL_OFFSET); - - loopcount=0; - while(loopcount++ < 10000 && - readb(dev->mem_start + COMX_JAIL_OFFSET) != COMX_JAIL_VALUE) { - udelay(100); - } - - if (readb(dev->mem_start + COMX_JAIL_OFFSET) != COMX_JAIL_VALUE) { - printk(KERN_ERR "%s: Can't reset board, JAIL value is %02x\n", - dev->name, readb(dev->mem_start + COMX_JAIL_OFFSET)); - retval=-ENODEV; - goto out; - } - - writeb(0x55, dev->mem_start + 0x18ff); - - loopcount=0; - while(loopcount++ < 10000 && readb(dev->mem_start + 0x18ff) != 0) { - udelay(100); - } - - if(readb(dev->mem_start + 0x18ff) != 0) { - printk(KERN_ERR "%s: Can't reset board, reset timeout\n", - dev->name); - retval=-ENODEV; - goto out; - } - - len = 0; - COMX_address = (byte *)dev->mem_start; - while (fw->len > len) { - writeb(fw->data[len++], COMX_address++); - } - - len = 0; - COMX_address = (byte *)dev->mem_start; - while (len != fw->len && readb(COMX_address++) == fw->data[len]) { - len++; - } - - if (len != fw->len) { - printk(KERN_ERR "%s: error loading firmware: [%d] is 0x%02x " - "instead of 0x%02x\n", dev->name, len, - readb(COMX_address - 1), fw->data[len]); - retval=-EAGAIN; - goto out; - } - - writeb(0, dev->mem_start + COMX_JAIL_OFFSET); - - loopcount = 0; - while ( loopcount++ < 10000 && COMX_readw(dev, OFF_A_L2_LINKUP) != 1 ) { - udelay(100); - } - - if (COMX_readw(dev, OFF_A_L2_LINKUP) != 1) { - printk(KERN_ERR "%s: error starting firmware, linkup word is %04x\n", - dev->name, COMX_readw(dev, OFF_A_L2_LINKUP)); - retval=-EAGAIN; - goto out; - } - - - ch->init_status |= FW_LOADED; - retval=0; - -out: - outb_p(board_segment | COMX_DISABLE_ALL, dev->base_addr); - if(saved) { - ((struct comx_channel *)saved->priv)->HW_board_on(saved); - } - memory_used[mempos]=saved; - restore_flags(flags); - return retval; -} - -static int CMX_load_board(struct net_device *dev) -{ - struct comx_channel *ch = dev->priv; - struct comx_privdata *hw = ch->HW_privdata; - struct comx_firmware *fw = hw->firmware; - word board_segment = dev->mem_start >> 16; - int mempos = (dev->mem_start - COMX_MEM_MIN) >> 16; - #if 0 - unsigned char id1, id2; - #endif - struct net_device *saved; - unsigned long flags; - int retval; - int loopcount; - int len; - byte *COMX_address; - - if (!fw || !fw->len) { - struct comx_channel *twin_ch = ch->twin ? ch->twin->priv : NULL; - struct comx_privdata *twin_hw; - - if (!twin_ch || !(twin_hw = twin_ch->HW_privdata)) { - return -EAGAIN; - } - - if (!(fw = twin_hw->firmware) || !fw->len) { - return -EAGAIN; - } - } - - /* Ide kell olyat tenni, hogy ellenorizze az ID-t */ - - if (inb_p(dev->base_addr) != CMX_ID_BYTE) { - printk(KERN_ERR "%s: CMX id byte is invalid(%02x)\n", dev->name, - inb_p(dev->base_addr)); - return -ENODEV; - } - - printk(KERN_INFO "%s: Loading CMX Layer 1 firmware %s\n", dev->name, - (char *)(fw->data + OFF_FW_L1_ID + 2)); - - save_flags(flags); cli(); - saved=memory_used[mempos]; - if(saved) { - ((struct comx_channel *)saved->priv)->HW_board_off(saved); - } - memory_used[mempos]=dev; - - outb_p(board_segment | COMX_ENABLE_BOARD_MEM | COMX_BOARD_RESET, - dev->base_addr); - - len = 0; - COMX_address = (byte *)dev->mem_start; - while (fw->len > len) { - writeb(fw->data[len++], COMX_address++); - } - - len = 0; - COMX_address = (byte *)dev->mem_start; - while (len != fw->len && readb(COMX_address++) == fw->data[len]) { - len++; - } - - outb_p(board_segment | COMX_ENABLE_BOARD_MEM, dev->base_addr); - - if (len != fw->len) { - printk(KERN_ERR "%s: error loading firmware: [%d] is 0x%02x " - "instead of 0x%02x\n", dev->name, len, - readb(COMX_address - 1), fw->data[len]); - retval=-EAGAIN; - goto out; - } - - loopcount=0; - while( loopcount++ < 10000 && COMX_readw(dev, OFF_A_L2_LINKUP) != 1 ) { - udelay(100); - } - - if (COMX_readw(dev, OFF_A_L2_LINKUP) != 1) { - printk(KERN_ERR "%s: error starting firmware, linkup word is %04x\n", - dev->name, COMX_readw(dev, OFF_A_L2_LINKUP)); - retval=-EAGAIN; - goto out; - } - - ch->init_status |= FW_LOADED; - retval=0; - -out: - outb_p(board_segment | COMX_DISABLE_ALL, dev->base_addr); - if(saved) { - ((struct comx_channel *)saved->priv)->HW_board_on(saved); - } - memory_used[mempos]=saved; - restore_flags(flags); - return retval; -} - -static int HICOMX_load_board(struct net_device *dev) -{ - struct comx_channel *ch = dev->priv; - struct comx_privdata *hw = ch->HW_privdata; - struct comx_firmware *fw = hw->firmware; - word board_segment = dev->mem_start >> 12; - int mempos = (dev->mem_start - COMX_MEM_MIN) >> 16; - struct net_device *saved; - unsigned char id1, id2; - unsigned long flags; - int retval; - int loopcount; - int len; - word *HICOMX_address; - char id = 1; - - if (!fw || !fw->len) { - struct comx_channel *twin_ch = ch->twin ? ch->twin->priv : NULL; - struct comx_privdata *twin_hw; - - if (!twin_ch || !(twin_hw = twin_ch->HW_privdata)) { - return -EAGAIN; - } - - if (!(fw = twin_hw->firmware) || !fw->len) { - return -EAGAIN; - } - } - - while (id != 4) { - if (inb_p(dev->base_addr + id++) != HICOMX_ID_BYTE) { - break; - } - } - - if (id != 4) { - printk(KERN_ERR "%s: can't find HICOMX at 0x%04x, id[%d] = %02x\n", - dev->name, (unsigned int)dev->base_addr, id - 1, - inb_p(dev->base_addr + id - 1)); - return -1; - } - - id1 = fw->data[OFF_FW_L1_ID]; - id2 = fw->data[OFF_FW_L1_ID + 1]; - if (id1 != FW_L1_ID_1 || id2 != FW_L1_ID_2_HICOMX) { - printk(KERN_ERR "%s: incorrect firmware, load aborted\n", dev->name); - return -EAGAIN; - } - - printk(KERN_INFO "%s: Loading HICOMX Layer 1 firmware %s\n", dev->name, - (char *)(fw->data + OFF_FW_L1_ID + 2)); - - id1 = fw->data[OFF_FW_L2_ID]; - id2 = fw->data[OFF_FW_L2_ID + 1]; - if (id1 == FW_L2_ID_1 && (id2 == 0xc0 || id2 == 0xc1 || id2 == 0xc2)) { - printk(KERN_INFO "with Layer 2 code %s\n", - (char *)(fw->data + OFF_FW_L2_ID + 2)); - } - - outb_p(board_segment | HICOMX_BOARD_RESET, dev->base_addr); - udelay(10); - - save_flags(flags); cli(); - saved=memory_used[mempos]; - if(saved) { - ((struct comx_channel *)saved->priv)->HW_board_off(saved); - } - memory_used[mempos]=dev; - - outb_p(board_segment | HICOMX_ENABLE_BOARD_MEM, dev->base_addr); - outb_p(HICOMX_PRG_MEM, dev->base_addr + 1); - - len = 0; - HICOMX_address = (word *)dev->mem_start; - while (fw->len > len) { - writeb(fw->data[len++], HICOMX_address++); - } - - len = 0; - HICOMX_address = (word *)dev->mem_start; - while (len != fw->len && (readw(HICOMX_address++) & 0xff) == fw->data[len]) { - len++; - } - - if (len != fw->len) { - printk(KERN_ERR "%s: error loading firmware: [%d] is 0x%02x " - "instead of 0x%02x\n", dev->name, len, - readw(HICOMX_address - 1) & 0xff, fw->data[len]); - retval=-EAGAIN; - goto out; - } - - outb_p(board_segment | HICOMX_BOARD_RESET, dev->base_addr); - outb_p(HICOMX_DATA_MEM, dev->base_addr + 1); - - outb_p(board_segment | HICOMX_ENABLE_BOARD_MEM, dev->base_addr); - - loopcount=0; - while(loopcount++ < 10000 && COMX_readw(dev, OFF_A_L2_LINKUP) != 1) { - udelay(100); - } - - if ( COMX_readw(dev, OFF_A_L2_LINKUP) != 1 ) { - printk(KERN_ERR "%s: error starting firmware, linkup word is %04x\n", - dev->name, COMX_readw(dev, OFF_A_L2_LINKUP)); - retval=-EAGAIN; - goto out; - } - - ch->init_status |= FW_LOADED; - retval=0; - -out: - outb_p(board_segment | HICOMX_DISABLE_ALL, dev->base_addr); - outb_p(HICOMX_DATA_MEM, dev->base_addr + 1); - - if(saved) { - ((struct comx_channel *)saved->priv)->HW_board_on(saved); - } - memory_used[mempos]=saved; - restore_flags(flags); - return retval; -} - -static struct net_device *comx_twin_check(struct net_device *dev) -{ - struct comx_channel *ch = dev->priv; - struct proc_dir_entry *procfile = ch->procdir->parent->subdir; - struct comx_privdata *hw = ch->HW_privdata; - - struct net_device *twin; - struct comx_channel *ch_twin; - struct comx_privdata *hw_twin; - - - for ( ; procfile ; procfile = procfile->next) { - - if(!S_ISDIR(procfile->mode)) { - continue; - } - - twin=procfile->data; - ch_twin=twin->priv; - hw_twin=ch_twin->HW_privdata; - - - if (twin != dev && dev->irq && dev->base_addr && dev->mem_start && - dev->irq == twin->irq && dev->base_addr == twin->base_addr && - dev->mem_start == twin->mem_start && - hw->channel == (1 - hw_twin->channel) && - ch->hardware == ch_twin->hardware) { - return twin; - } - } - return NULL; -} - -static int comxhw_write_proc(struct file *file, const char *buffer, - u_long count, void *data) -{ - struct proc_dir_entry *entry = (struct proc_dir_entry *)data; - struct net_device *dev = entry->parent->data; - struct comx_channel *ch = dev->priv; - struct comx_privdata *hw = ch->HW_privdata; - char *page; - - - if(ch->init_status & HW_OPEN) { - return -EAGAIN; - } - - if (strcmp(FILENAME_FIRMWARE, entry->name) != 0) { - if (!(page = (char *)__get_free_page(GFP_KERNEL))) { - return -ENOMEM; - } - if(copy_from_user(page, buffer, count = (min_t(int, count, PAGE_SIZE)))) - { - count = -EFAULT; - goto out; - } - if (page[count-1] == '\n') - page[count-1] = '\0'; - else if (count < PAGE_SIZE) - page[count] = '\0'; - else if (page[count]) { - count = -EINVAL; - goto out; - } - page[count]=0; /* Null terminate */ - } else { - byte *tmp; - - if (!hw->firmware) { - if ((hw->firmware = kmalloc(sizeof(struct comx_firmware), - GFP_KERNEL)) == NULL) { - return -ENOMEM; - } - hw->firmware->len = 0; - hw->firmware->data = NULL; - } - - if ((tmp = kmalloc(count + file->f_pos, GFP_KERNEL)) == NULL) { - return -ENOMEM; - } - - /* Ha nem 0 a fpos, akkor meglevo file-t irunk. Gyenge trukk. */ - if (hw->firmware && hw->firmware->len && file->f_pos - && hw->firmware->len < count + file->f_pos) { - memcpy(tmp, hw->firmware->data, hw->firmware->len); - } - if (hw->firmware->data) { - kfree(hw->firmware->data); - } - if (copy_from_user(tmp + file->f_pos, buffer, count)) - return -EFAULT; - hw->firmware->len = entry->size = file->f_pos + count; - hw->firmware->data = tmp; - file->f_pos += count; - return count; - } - - if (strcmp(entry->name, FILENAME_CHANNEL) == 0) { - hw->channel = simple_strtoul(page, NULL, 0); - if (hw->channel >= MAX_CHANNELNO) { - printk(KERN_ERR "Invalid channel number\n"); - hw->channel = 0; - } - if ((ch->twin = comx_twin_check(dev)) != NULL) { - struct comx_channel *twin_ch = ch->twin->priv; - twin_ch->twin = dev; - } - } else if (strcmp(entry->name, FILENAME_IRQ) == 0) { - dev->irq = simple_strtoul(page, NULL, 0); - if (dev->irq == 2) { - dev->irq = 9; - } - if (dev->irq < 3 || dev->irq > 15) { - printk(KERN_ERR "comxhw: Invalid irq number\n"); - dev->irq = 0; - } - if ((ch->twin = comx_twin_check(dev)) != NULL) { - struct comx_channel *twin_ch = ch->twin->priv; - twin_ch->twin = dev; - } - } else if (strcmp(entry->name, FILENAME_IO) == 0) { - dev->base_addr = simple_strtoul(page, NULL, 0); - if ((dev->base_addr & 3) != 0 || dev->base_addr < 0x300 - || dev->base_addr > 0x3fc) { - printk(KERN_ERR "Invalid io value\n"); - dev->base_addr = 0; - } - if ((ch->twin = comx_twin_check(dev)) != NULL) { - struct comx_channel *twin_ch = ch->twin->priv; - - twin_ch->twin = dev; - } - } else if (strcmp(entry->name, FILENAME_MEMADDR) == 0) { - dev->mem_start = simple_strtoul(page, NULL, 0); - if (dev->mem_start <= 0xf000 && dev->mem_start >= 0xa000) { - dev->mem_start *= 16; - } - if ((dev->mem_start & 0xfff) != 0 || dev->mem_start < COMX_MEM_MIN - || dev->mem_start + hw->memory_size > COMX_MEM_MAX) { - printk(KERN_ERR "Invalid memory page\n"); - dev->mem_start = 0; - } - dev->mem_end = dev->mem_start + hw->memory_size; - if ((ch->twin = comx_twin_check(dev)) != NULL) { - struct comx_channel *twin_ch = ch->twin->priv; - - twin_ch->twin = dev; - } - } else if (strcmp(entry->name, FILENAME_CLOCK) == 0) { - if (strncmp("ext", page, 3) == 0) { - hw->clock = 0; - } else { - int kbps; - - kbps = simple_strtoul(page, NULL, 0); - hw->clock = kbps ? COMX_CLOCK_CONST/kbps : 0; - } - } -out: - free_page((unsigned long)page); - return count; -} - -static int comxhw_read_proc(char *page, char **start, off_t off, int count, - int *eof, void *data) -{ - struct proc_dir_entry *file = (struct proc_dir_entry *)data; - struct net_device *dev = file->parent->data; - struct comx_channel *ch = dev->priv; - struct comx_privdata *hw = ch->HW_privdata; - int len = 0; - - - if (strcmp(file->name, FILENAME_IO) == 0) { - len = sprintf(page, "0x%03x\n", (unsigned int)dev->base_addr); - } else if (strcmp(file->name, FILENAME_IRQ) == 0) { - len = sprintf(page, "0x%02x\n", dev->irq == 9 ? 2 : dev->irq); - } else if (strcmp(file->name, FILENAME_CHANNEL) == 0) { - len = sprintf(page, "%01d\n", hw->channel); - } else if (strcmp(file->name, FILENAME_MEMADDR) == 0) { - len = sprintf(page, "0x%05x\n", (unsigned int)dev->mem_start); - } else if (strcmp(file->name, FILENAME_TWIN) == 0) { - len = sprintf(page, "%s\n", ch->twin ? ch->twin->name : "none"); - } else if (strcmp(file->name, FILENAME_CLOCK) == 0) { - if (hw->clock) { - len = sprintf(page, "%-8d\n", COMX_CLOCK_CONST/hw->clock); - } else { - len = sprintf(page, "external\n"); - } - } else if (strcmp(file->name, FILENAME_FIRMWARE) == 0) { - len = min_t(int, FILE_PAGESIZE, - min_t(int, count, - hw->firmware ? - (hw->firmware->len - off) : 0)); - if (len < 0) { - len = 0; - } - *start = hw->firmware ? (hw->firmware->data + off) : NULL; - if (off + len >= (hw->firmware ? hw->firmware->len : 0) || len == 0) { - *eof = 1; - } - return len; - } - - if (off >= len) { - *eof = 1; - return 0; - } - - *start = page + off; - if (count >= len - off) { - *eof = 1; - } - return min_t(int, count, len - off); -} - -/* Called on echo comx >boardtype */ -static int COMX_init(struct net_device *dev) -{ - struct comx_channel *ch = dev->priv; - struct comx_privdata *hw; - struct proc_dir_entry *new_file; - - if ((ch->HW_privdata = kmalloc(sizeof(struct comx_privdata), - GFP_KERNEL)) == NULL) { - return -ENOMEM; - } - memset(hw = ch->HW_privdata, 0, sizeof(struct comx_privdata)); - - if (ch->hardware == &comx_hw || ch->hardware == &cmx_hw) { - hw->memory_size = COMX_MEMORY_SIZE; - hw->io_extent = COMX_IO_EXTENT; - dev->base_addr = COMX_DEFAULT_IO; - dev->irq = COMX_DEFAULT_IRQ; - dev->mem_start = COMX_DEFAULT_MEMADDR; - dev->mem_end = COMX_DEFAULT_MEMADDR + COMX_MEMORY_SIZE; - } else if (ch->hardware == &hicomx_hw) { - hw->memory_size = HICOMX_MEMORY_SIZE; - hw->io_extent = HICOMX_IO_EXTENT; - dev->base_addr = HICOMX_DEFAULT_IO; - dev->irq = HICOMX_DEFAULT_IRQ; - dev->mem_start = HICOMX_DEFAULT_MEMADDR; - dev->mem_end = HICOMX_DEFAULT_MEMADDR + HICOMX_MEMORY_SIZE; - } else { - printk(KERN_ERR "SERIOUS INTERNAL ERROR in %s, line %d\n", __FILE__, __LINE__); - } - - if ((new_file = create_proc_entry(FILENAME_IO, S_IFREG | 0644, ch->procdir)) - == NULL) { - goto cleanup_HW_privdata; - } - new_file->data = (void *)new_file; - new_file->read_proc = &comxhw_read_proc; - new_file->write_proc = &comxhw_write_proc; - new_file->size = 6; - new_file->nlink = 1; - - if ((new_file = create_proc_entry(FILENAME_IRQ, S_IFREG | 0644, ch->procdir)) - == NULL) { - goto cleanup_filename_io; - } - new_file->data = (void *)new_file; - new_file->read_proc = &comxhw_read_proc; - new_file->write_proc = &comxhw_write_proc; - new_file->size = 5; - new_file->nlink = 1; - - if ((new_file = create_proc_entry(FILENAME_CHANNEL, S_IFREG | 0644, - ch->procdir)) == NULL) { - goto cleanup_filename_irq; - } - new_file->data = (void *)new_file; - new_file->read_proc = &comxhw_read_proc; - new_file->write_proc = &comxhw_write_proc; - new_file->size = 2; // Ezt tudjuk - new_file->nlink = 1; - - if (ch->hardware == &hicomx_hw || ch->hardware == &cmx_hw) { - if ((new_file = create_proc_entry(FILENAME_CLOCK, S_IFREG | 0644, - ch->procdir)) == NULL) { - goto cleanup_filename_channel; - } - new_file->data = (void *)new_file; - new_file->read_proc = &comxhw_read_proc; - new_file->write_proc = &comxhw_write_proc; - new_file->size = 9; - new_file->nlink = 1; - } - - if ((new_file = create_proc_entry(FILENAME_MEMADDR, S_IFREG | 0644, - ch->procdir)) == NULL) { - goto cleanup_filename_clock; - } - new_file->data = (void *)new_file; - new_file->read_proc = &comxhw_read_proc; - new_file->write_proc = &comxhw_write_proc; - new_file->size = 8; - new_file->nlink = 1; - - if ((new_file = create_proc_entry(FILENAME_TWIN, S_IFREG | 0444, - ch->procdir)) == NULL) { - goto cleanup_filename_memaddr; - } - new_file->data = (void *)new_file; - new_file->read_proc = &comxhw_read_proc; - new_file->write_proc = NULL; - new_file->nlink = 1; - - if ((new_file = create_proc_entry(FILENAME_FIRMWARE, S_IFREG | 0644, - ch->procdir)) == NULL) { - goto cleanup_filename_twin; - } - new_file->data = (void *)new_file; - new_file->read_proc = &comxhw_read_proc; - new_file->write_proc = &comxhw_write_proc; - new_file->nlink = 1; - - if (ch->hardware == &comx_hw) { - ch->HW_board_on = COMX_board_on; - ch->HW_board_off = COMX_board_off; - ch->HW_load_board = COMX_load_board; - } else if (ch->hardware == &cmx_hw) { - ch->HW_board_on = COMX_board_on; - ch->HW_board_off = COMX_board_off; - ch->HW_load_board = CMX_load_board; - ch->HW_set_clock = COMX_set_clock; - } else if (ch->hardware == &hicomx_hw) { - ch->HW_board_on = HICOMX_board_on; - ch->HW_board_off = HICOMX_board_off; - ch->HW_load_board = HICOMX_load_board; - ch->HW_set_clock = COMX_set_clock; - } else { - printk(KERN_ERR "SERIOUS INTERNAL ERROR in %s, line %d\n", __FILE__, __LINE__); - } - - ch->HW_access_board = COMX_access_board; - ch->HW_release_board = COMX_release_board; - ch->HW_txe = COMX_txe; - ch->HW_open = COMX_open; - ch->HW_close = COMX_close; - ch->HW_send_packet = COMX_send_packet; - ch->HW_statistics = COMX_statistics; - - if ((ch->twin = comx_twin_check(dev)) != NULL) { - struct comx_channel *twin_ch = ch->twin->priv; - - twin_ch->twin = dev; - } - - MOD_INC_USE_COUNT; - return 0; - -cleanup_filename_twin: - remove_proc_entry(FILENAME_TWIN, ch->procdir); -cleanup_filename_memaddr: - remove_proc_entry(FILENAME_MEMADDR, ch->procdir); -cleanup_filename_clock: - if (ch->hardware == &hicomx_hw || ch->hardware == &cmx_hw) - remove_proc_entry(FILENAME_CLOCK, ch->procdir); -cleanup_filename_channel: - remove_proc_entry(FILENAME_CHANNEL, ch->procdir); -cleanup_filename_irq: - remove_proc_entry(FILENAME_IRQ, ch->procdir); -cleanup_filename_io: - remove_proc_entry(FILENAME_IO, ch->procdir); -cleanup_HW_privdata: - kfree(ch->HW_privdata); - return -EIO; -} - -/* Called on echo valami >boardtype */ -static int COMX_exit(struct net_device *dev) -{ - struct comx_channel *ch = dev->priv; - struct comx_privdata *hw = ch->HW_privdata; - - if (hw->firmware) { - if (hw->firmware->data) kfree(hw->firmware->data); - kfree(hw->firmware); - } if (ch->twin) { - struct comx_channel *twin_ch = ch->twin->priv; - - twin_ch->twin = NULL; - } - - kfree(ch->HW_privdata); - remove_proc_entry(FILENAME_IO, ch->procdir); - remove_proc_entry(FILENAME_IRQ, ch->procdir); - remove_proc_entry(FILENAME_CHANNEL, ch->procdir); - remove_proc_entry(FILENAME_MEMADDR, ch->procdir); - remove_proc_entry(FILENAME_FIRMWARE, ch->procdir); - remove_proc_entry(FILENAME_TWIN, ch->procdir); - if (ch->hardware == &hicomx_hw || ch->hardware == &cmx_hw) { - remove_proc_entry(FILENAME_CLOCK, ch->procdir); - } - - MOD_DEC_USE_COUNT; - return 0; -} - -static int COMX_dump(struct net_device *dev) -{ - printk(KERN_INFO "%s: COMX_dump called, why ?\n", dev->name); - return 0; -} - -static struct comx_hardware comx_hw = { - "comx", - VERSION, - COMX_init, - COMX_exit, - COMX_dump, - NULL -}; - -static struct comx_hardware cmx_hw = { - "cmx", - VERSION, - COMX_init, - COMX_exit, - COMX_dump, - NULL -}; - -static struct comx_hardware hicomx_hw = { - "hicomx", - VERSION, - COMX_init, - COMX_exit, - COMX_dump, - NULL -}; - -static int __init comx_hw_comx_init(void) -{ - comx_register_hardware(&comx_hw); - comx_register_hardware(&cmx_hw); - comx_register_hardware(&hicomx_hw); - return 0; -} - -static void __exit comx_hw_comx_exit(void) -{ - comx_unregister_hardware("comx"); - comx_unregister_hardware("cmx"); - comx_unregister_hardware("hicomx"); -} - -module_init(comx_hw_comx_init); -module_exit(comx_hw_comx_exit); diff --git a/drivers/net/wan/comx-hw-locomx.c b/drivers/net/wan/comx-hw-locomx.c deleted file mode 100644 index 52460164a..000000000 --- a/drivers/net/wan/comx-hw-locomx.c +++ /dev/null @@ -1,496 +0,0 @@ -/* - * Hardware driver for the LoCOMX card, using the generic z85230 - * functions - * - * Author: Gergely Madarasz - * - * Based on skeleton code and old LoCOMX driver by Tivadar Szemethy - * and the hostess_sv11 driver - * - * Contributors: - * Arnaldo Carvalho de Melo (0.14) - * - * Copyright (C) 1999 ITConsult-Pro Co. - * - * 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. - * - * Version 0.10 (99/06/17): - * - rewritten for the z85230 layer - * - * Version 0.11 (99/06/21): - * - some printk's fixed - * - get rid of a memory leak (it was impossible though :)) - * - * Version 0.12 (99/07/07): - * - check CTS for modem lines, not DCD (which is always high - * in case of this board) - * Version 0.13 (99/07/08): - * - Fix the transmitter status check - * - Handle the net device statistics better - * Version 0.14 (00/08/15): - * - resource release on failure at LOCOMX_init - */ - -#define VERSION "0.14" - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "comx.h" -#include "z85230.h" - -MODULE_AUTHOR("Gergely Madarasz "); -MODULE_DESCRIPTION("Hardware driver for the LoCOMX board"); -MODULE_LICENSE("GPL"); - -#define RX_DMA 3 -#define TX_DMA 1 -#define LOCOMX_ID 0x33 -#define LOCOMX_IO_EXTENT 8 -#define LOCOMX_DEFAULT_IO 0x368 -#define LOCOMX_DEFAULT_IRQ 7 - -u8 z8530_locomx[] = { - 11, TCRTxCP, - 14, DTRREQ, - 255 -}; - -struct locomx_data { - int io_extent; - struct z8530_dev board; - struct timer_list status_timer; -}; - -static int LOCOMX_txe(struct net_device *dev) -{ - struct comx_channel *ch = netdev_priv(dev); - struct locomx_data *hw = ch->HW_privdata; - - return (!hw->board.chanA.tx_next_skb); -} - - -static void locomx_rx(struct z8530_channel *c, struct sk_buff *skb) -{ - struct net_device *dev = c->netdevice; - struct comx_channel *ch = netdev_priv(dev); - - if (ch->debug_flags & DEBUG_HW_RX) { - comx_debug_skb(dev, skb, "locomx_rx receiving"); - } - ch->LINE_rx(dev,skb); -} - -static int LOCOMX_send_packet(struct net_device *dev, struct sk_buff *skb) -{ - struct comx_channel *ch = netdev_priv(dev); - struct locomx_data *hw = ch->HW_privdata; - - if (ch->debug_flags & DEBUG_HW_TX) { - comx_debug_bytes(dev, skb->data, skb->len, "LOCOMX_send_packet"); - } - - if (!(ch->line_status & LINE_UP)) { - return FRAME_DROPPED; - } - - if(z8530_queue_xmit(&hw->board.chanA,skb)) { - printk(KERN_WARNING "%s: FRAME_DROPPED\n",dev->name); - return FRAME_DROPPED; - } - - if (ch->debug_flags & DEBUG_HW_TX) { - comx_debug(dev, "%s: LOCOMX_send_packet was successful\n\n", dev->name); - } - - if(!hw->board.chanA.tx_next_skb) { - return FRAME_QUEUED; - } else { - return FRAME_ACCEPTED; - } -} - -static void locomx_status_timerfun(unsigned long d) -{ - struct net_device *dev = (struct net_device *)d; - struct comx_channel *ch = netdev_priv(dev); - struct locomx_data *hw = ch->HW_privdata; - - if(!(ch->line_status & LINE_UP) && - (hw->board.chanA.status & CTS)) { - ch->LINE_status(dev, ch->line_status | LINE_UP); - } - if((ch->line_status & LINE_UP) && - !(hw->board.chanA.status & CTS)) { - ch->LINE_status(dev, ch->line_status & ~LINE_UP); - } - mod_timer(&hw->status_timer,jiffies + ch->lineup_delay * HZ); -} - - -static int LOCOMX_open(struct net_device *dev) -{ - struct comx_channel *ch = netdev_priv(dev); - struct locomx_data *hw = ch->HW_privdata; - struct proc_dir_entry *procfile = ch->procdir->subdir; - unsigned long flags; - int ret; - - if (!dev->base_addr || !dev->irq) { - return -ENODEV; - } - - if (!request_region(dev->base_addr, hw->io_extent, dev->name)) { - return -EAGAIN; - } - - hw->board.chanA.ctrlio=dev->base_addr + 5; - hw->board.chanA.dataio=dev->base_addr + 7; - - hw->board.irq=dev->irq; - hw->board.chanA.netdevice=dev; - hw->board.chanA.dev=&hw->board; - hw->board.name=dev->name; - hw->board.chanA.txdma=TX_DMA; - hw->board.chanA.rxdma=RX_DMA; - hw->board.chanA.irqs=&z8530_nop; - hw->board.chanB.irqs=&z8530_nop; - - if(request_irq(dev->irq, z8530_interrupt, SA_INTERRUPT, - dev->name, &hw->board)) { - printk(KERN_ERR "%s: unable to obtain irq %d\n", dev->name, - dev->irq); - ret=-EAGAIN; - goto irq_fail; - } - if(request_dma(TX_DMA,"LoCOMX (TX)")) { - printk(KERN_ERR "%s: unable to obtain TX DMA (DMA channel %d)\n", - dev->name, TX_DMA); - ret=-EAGAIN; - goto dma1_fail; - } - - if(request_dma(RX_DMA,"LoCOMX (RX)")) { - printk(KERN_ERR "%s: unable to obtain RX DMA (DMA channel %d)\n", - dev->name, RX_DMA); - ret=-EAGAIN; - goto dma2_fail; - } - - save_flags(flags); - cli(); - - if(z8530_init(&hw->board)!=0) - { - printk(KERN_ERR "%s: Z8530 device not found.\n",dev->name); - ret=-ENODEV; - goto z8530_fail; - } - - hw->board.chanA.dcdcheck=CTS; - - z8530_channel_load(&hw->board.chanA, z8530_hdlc_kilostream_85230); - z8530_channel_load(&hw->board.chanA, z8530_locomx); - z8530_channel_load(&hw->board.chanB, z8530_dead_port); - - z8530_describe(&hw->board, "I/O", dev->base_addr); - - if((ret=z8530_sync_dma_open(dev, &hw->board.chanA))!=0) { - goto z8530_fail; - } - - restore_flags(flags); - - - hw->board.active=1; - hw->board.chanA.rx_function=locomx_rx; - - ch->init_status |= HW_OPEN; - if (hw->board.chanA.status & DCD) { - ch->line_status |= LINE_UP; - } else { - ch->line_status &= ~LINE_UP; - } - - comx_status(dev, ch->line_status); - - init_timer(&hw->status_timer); - hw->status_timer.function=locomx_status_timerfun; - hw->status_timer.data=(unsigned long)dev; - hw->status_timer.expires=jiffies + ch->lineup_delay * HZ; - add_timer(&hw->status_timer); - - for (; procfile ; procfile = procfile->next) { - if (strcmp(procfile->name, FILENAME_IO) == 0 || - strcmp(procfile->name, FILENAME_IRQ) == 0) { - procfile->mode = S_IFREG | 0444; - } - } - return 0; - -z8530_fail: - restore_flags(flags); - free_dma(RX_DMA); -dma2_fail: - free_dma(TX_DMA); -dma1_fail: - free_irq(dev->irq, &hw->board); -irq_fail: - release_region(dev->base_addr, hw->io_extent); - return ret; -} - -static int LOCOMX_close(struct net_device *dev) -{ - struct comx_channel *ch = netdev_priv(dev); - struct locomx_data *hw = ch->HW_privdata; - struct proc_dir_entry *procfile = ch->procdir->subdir; - - hw->board.chanA.rx_function=z8530_null_rx; - netif_stop_queue(dev); - z8530_sync_dma_close(dev, &hw->board.chanA); - - z8530_shutdown(&hw->board); - - del_timer(&hw->status_timer); - free_dma(RX_DMA); - free_dma(TX_DMA); - free_irq(dev->irq,&hw->board); - release_region(dev->base_addr,8); - - for (; procfile ; procfile = procfile->next) { - if (strcmp(procfile->name, FILENAME_IO) == 0 || - strcmp(procfile->name, FILENAME_IRQ) == 0) { - procfile->mode = S_IFREG | 0644; - } - } - - ch->init_status &= ~HW_OPEN; - return 0; -} - -static int LOCOMX_statistics(struct net_device *dev,char *page) -{ - int len = 0; - - len += sprintf(page + len, "Hello\n"); - - return len; -} - -static int LOCOMX_dump(struct net_device *dev) { - printk(KERN_INFO "LOCOMX_dump called\n"); - return(-1); -} - -static int locomx_read_proc(char *page, char **start, off_t off, int count, - int *eof, void *data) -{ - struct proc_dir_entry *file = (struct proc_dir_entry *)data; - struct net_device *dev = file->parent->data; - int len = 0; - - if (strcmp(file->name, FILENAME_IO) == 0) { - len = sprintf(page, "0x%x\n", (unsigned int)dev->base_addr); - } else if (strcmp(file->name, FILENAME_IRQ) == 0) { - len = sprintf(page, "%d\n", (unsigned int)dev->irq); - } else { - printk(KERN_ERR "hw_read_proc: internal error, filename %s\n", - file->name); - return -EBADF; - } - - if (off >= len) { - *eof = 1; - return 0; - } - - *start = page + off; - if (count >= len - off) { - *eof = 1; - } - return min_t(int, count, len - off); -} - -static int locomx_write_proc(struct file *file, const char *buffer, - u_long count, void *data) -{ - struct proc_dir_entry *entry = (struct proc_dir_entry *)data; - struct net_device *dev = (struct net_device *)entry->parent->data; - int val; - char *page; - - if (!(page = (char *)__get_free_page(GFP_KERNEL))) { - return -ENOMEM; - } - - if (copy_from_user(page, buffer, count = min_t(unsigned long, count, PAGE_SIZE))) { - free_page((unsigned long)page); - return -EBADF; - } - if (*(page + count - 1) == '\n') { - *(page + count - 1) = 0; - } - - if (strcmp(entry->name, FILENAME_IO) == 0) { - val = simple_strtoul(page, NULL, 0); - if (val != 0x360 && val != 0x368 && val != 0x370 && - val != 0x378) { - printk(KERN_ERR "LoCOMX: incorrect io address!\n"); - } else { - dev->base_addr = val; - } - } else if (strcmp(entry->name, FILENAME_IRQ) == 0) { - val = simple_strtoul(page, NULL, 0); - if (val != 3 && val != 4 && val != 5 && val != 6 && val != 7) { - printk(KERN_ERR "LoCOMX: incorrect irq value!\n"); - } else { - dev->irq = val; - } - } else { - printk(KERN_ERR "locomx_write_proc: internal error, filename %s\n", - entry->name); - free_page((unsigned long)page); - return -EBADF; - } - - free_page((unsigned long)page); - return count; -} - - - -static int LOCOMX_init(struct net_device *dev) -{ - struct comx_channel *ch = netdev_priv(dev); - struct locomx_data *hw; - struct proc_dir_entry *new_file; - - /* Alloc data for private structure */ - if ((ch->HW_privdata = kmalloc(sizeof(struct locomx_data), - GFP_KERNEL)) == NULL) { - return -ENOMEM; - } - - memset(hw = ch->HW_privdata, 0, sizeof(struct locomx_data)); - hw->io_extent = LOCOMX_IO_EXTENT; - - /* Register /proc files */ - if ((new_file = create_proc_entry(FILENAME_IO, S_IFREG | 0644, - ch->procdir)) == NULL) { - goto cleanup_HW_privdata; - } - new_file->data = (void *)new_file; - new_file->read_proc = &locomx_read_proc; - new_file->write_proc = &locomx_write_proc; - new_file->nlink = 1; - - if ((new_file = create_proc_entry(FILENAME_IRQ, S_IFREG | 0644, - ch->procdir)) == NULL) { - goto cleanup_filename_io; - } - new_file->data = (void *)new_file; - new_file->read_proc = &locomx_read_proc; - new_file->write_proc = &locomx_write_proc; - new_file->nlink = 1; - -/* No clock yet */ -/* - if ((new_file = create_proc_entry(FILENAME_CLOCK, S_IFREG | 0644, - ch->procdir)) == NULL) { - return -EIO; - } - new_file->data = (void *)new_file; - new_file->read_proc = &locomx_read_proc; - new_file->write_proc = &locomx_write_proc; - new_file->nlink = 1; -*/ - - ch->HW_access_board = NULL; - ch->HW_release_board = NULL; - ch->HW_txe = LOCOMX_txe; - ch->HW_open = LOCOMX_open; - ch->HW_close = LOCOMX_close; - ch->HW_send_packet = LOCOMX_send_packet; - ch->HW_statistics = LOCOMX_statistics; - ch->HW_set_clock = NULL; - - ch->current_stats = &hw->board.chanA.stats; - memcpy(ch->current_stats, &ch->stats, sizeof(struct net_device_stats)); - - dev->base_addr = LOCOMX_DEFAULT_IO; - dev->irq = LOCOMX_DEFAULT_IRQ; - - - /* O.K. Count one more user on this module */ - MOD_INC_USE_COUNT; - return 0; -cleanup_filename_io: - remove_proc_entry(FILENAME_IO, ch->procdir); -cleanup_HW_privdata: - kfree(ch->HW_privdata); - return -EIO; -} - - -static int LOCOMX_exit(struct net_device *dev) -{ - struct comx_channel *ch = netdev_priv(dev); - - ch->HW_access_board = NULL; - ch->HW_release_board = NULL; - ch->HW_txe = NULL; - ch->HW_open = NULL; - ch->HW_close = NULL; - ch->HW_send_packet = NULL; - ch->HW_statistics = NULL; - ch->HW_set_clock = NULL; - memcpy(&ch->stats, ch->current_stats, sizeof(struct net_device_stats)); - ch->current_stats = &ch->stats; - - kfree(ch->HW_privdata); - - remove_proc_entry(FILENAME_IO, ch->procdir); - remove_proc_entry(FILENAME_IRQ, ch->procdir); -// remove_proc_entry(FILENAME_CLOCK, ch->procdir); - - MOD_DEC_USE_COUNT; - return 0; -} - -static struct comx_hardware locomx_hw = { - "locomx", - VERSION, - LOCOMX_init, - LOCOMX_exit, - LOCOMX_dump, - NULL -}; - -static int __init comx_hw_locomx_init(void) -{ - comx_register_hardware(&locomx_hw); - return 0; -} - -static void __exit comx_hw_locomx_exit(void) -{ - comx_unregister_hardware("locomx"); -} - -module_init(comx_hw_locomx_init); -module_exit(comx_hw_locomx_exit); diff --git a/drivers/net/wan/comx-hw-mixcom.c b/drivers/net/wan/comx-hw-mixcom.c deleted file mode 100644 index c6fb9ac67..000000000 --- a/drivers/net/wan/comx-hw-mixcom.c +++ /dev/null @@ -1,960 +0,0 @@ -/* - * Hardware driver for the MixCom synchronous serial board - * - * Author: Gergely Madarasz - * - * based on skeleton driver code and a preliminary hscx driver by - * Tivadar Szemethy - * - * Copyright (C) 1998-1999 ITConsult-Pro Co. - * - * Contributors: - * Arnaldo Carvalho de Melo (0.65) - * - * 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. - * - * Version 0.60 (99/06/11): - * - ported to the kernel, now works as builtin code - * - * Version 0.61 (99/06/11): - * - recognize the one-channel MixCOM card (id byte = 0x13) - * - printk fixes - * - * Version 0.62 (99/07/15): - * - fixes according to the new hw docs - * - report line status when open - * - * Version 0.63 (99/09/21): - * - line status report fixes - * - * Version 0.64 (99/12/01): - * - some more cosmetical fixes - * - * Version 0.65 (00/08/15) - * - resource release on failure at MIXCOM_init - */ - -#define VERSION "0.65" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "comx.h" -#include "mixcom.h" -#include "hscx.h" - -MODULE_AUTHOR("Gergely Madarasz "); -MODULE_DESCRIPTION("Hardware-level driver for the serial port of the MixCom board"); -MODULE_LICENSE("GPL"); - -#define MIXCOM_DATA(d) ((struct mixcom_privdata *)(COMX_CHANNEL(d)-> \ - HW_privdata)) - -#define MIXCOM_BOARD_BASE(d) (d->base_addr - MIXCOM_SERIAL_OFFSET - \ - (1 - MIXCOM_DATA(d)->channel) * MIXCOM_CHANNEL_OFFSET) - -#define MIXCOM_DEV_BASE(port,channel) (port + MIXCOM_SERIAL_OFFSET + \ - (1 - channel) * MIXCOM_CHANNEL_OFFSET) - -/* Values used to set the IRQ line */ -static unsigned char mixcom_set_irq[]={0xFF, 0xFF, 0xFF, 0x0, 0xFF, 0x2, 0x4, 0x6, 0xFF, 0xFF, 0x8, 0xA, 0xC, 0xFF, 0xE, 0xFF}; - -static unsigned char* hscx_versions[]={"A1", NULL, "A2", NULL, "A3", "2.1"}; - -struct mixcom_privdata { - u16 clock; - char channel; - long txbusy; - struct sk_buff *sending; - unsigned tx_ptr; - struct sk_buff *recving; - unsigned rx_ptr; - unsigned char status; - char card_has_status; -}; - -static inline void wr_hscx(struct net_device *dev, int reg, unsigned char val) -{ - outb(val, dev->base_addr + reg); -} - -static inline unsigned char rd_hscx(struct net_device *dev, int reg) -{ - return inb(dev->base_addr + reg); -} - -static inline void hscx_cmd(struct net_device *dev, int cmd) -{ - unsigned long jiffs = jiffies; - unsigned char cec; - unsigned delay = 0; - - while ((cec = (rd_hscx(dev, HSCX_STAR) & HSCX_CEC) != 0) && - time_before(jiffies, jiffs + HZ)) { - udelay(1); - if (++delay > (100000 / HZ)) break; - } - if (cec) { - printk(KERN_WARNING "%s: CEC stuck, probably no clock!\n",dev->name); - } else { - wr_hscx(dev, HSCX_CMDR, cmd); - } -} - -static inline void hscx_fill_fifo(struct net_device *dev) -{ - struct comx_channel *ch = dev->priv; - struct mixcom_privdata *hw = ch->HW_privdata; - register word to_send = hw->sending->len - hw->tx_ptr; - - - outsb(dev->base_addr + HSCX_FIFO, - &(hw->sending->data[hw->tx_ptr]), min_t(unsigned int, to_send, 32)); - if (to_send <= 32) { - hscx_cmd(dev, HSCX_XTF | HSCX_XME); - kfree_skb(hw->sending); - hw->sending = NULL; - hw->tx_ptr = 0; - } else { - hscx_cmd(dev, HSCX_XTF); - hw->tx_ptr += 32; - } -} - -static inline void hscx_empty_fifo(struct net_device *dev, int cnt) -{ - struct comx_channel *ch = dev->priv; - struct mixcom_privdata *hw = ch->HW_privdata; - - if (hw->recving == NULL) { - if (!(hw->recving = dev_alloc_skb(HSCX_MTU + 16))) { - ch->stats.rx_dropped++; - hscx_cmd(dev, HSCX_RHR); - } else { - skb_reserve(hw->recving, 16); - skb_put(hw->recving, HSCX_MTU); - } - hw->rx_ptr = 0; - } - if (cnt > 32 || !cnt || hw->recving == NULL) { - printk(KERN_ERR "hscx_empty_fifo: cnt is %d, hw->recving %p\n", - cnt, (void *)hw->recving); - return; - } - - insb(dev->base_addr + HSCX_FIFO, &(hw->recving->data[hw->rx_ptr]),cnt); - hw->rx_ptr += cnt; - hscx_cmd(dev, HSCX_RMC); -} - - -static int MIXCOM_txe(struct net_device *dev) -{ - struct comx_channel *ch = dev->priv; - struct mixcom_privdata *hw = ch->HW_privdata; - - return !test_bit(0, &hw->txbusy); -} - -static int mixcom_probe(struct net_device *dev) -{ - unsigned long flags; - int id, vstr, ret=0; - - save_flags(flags); cli(); - - id=inb_p(MIXCOM_BOARD_BASE(dev) + MIXCOM_ID_OFFSET) & 0x7f; - - if (id != MIXCOM_ID ) { - ret=-ENODEV; - printk(KERN_WARNING "%s: no MixCOM board found at 0x%04lx\n",dev->name, dev->base_addr); - goto out; - } - - vstr=inb_p(dev->base_addr + HSCX_VSTR) & 0x0f; - if(vstr>=sizeof(hscx_versions)/sizeof(char*) || - hscx_versions[vstr]==NULL) { - printk(KERN_WARNING "%s: board found but no HSCX chip detected at 0x%4lx (vstr = 0x%1x)\n",dev->name,dev->base_addr,vstr); - ret = -ENODEV; - } else { - printk(KERN_INFO "%s: HSCX chip version %s\n",dev->name,hscx_versions[vstr]); - ret = 0; - } - -out: - - restore_flags(flags); - return ret; -} - -#if 0 -static void MIXCOM_set_clock(struct net_device *dev) -{ - struct comx_channel *ch = dev->priv; - struct mixcom_privdata *hw = ch->HW_privdata; - - if (hw->clock) { - ; - } else { - ; - } -} -#endif - -static void mixcom_board_on(struct net_device *dev) -{ - outb_p(MIXCOM_OFF , MIXCOM_BOARD_BASE(dev) + MIXCOM_IT_OFFSET); - udelay(1000); - outb_p(mixcom_set_irq[dev->irq] | MIXCOM_ON, - MIXCOM_BOARD_BASE(dev) + MIXCOM_IT_OFFSET); - udelay(1000); -} - -static void mixcom_board_off(struct net_device *dev) -{ - outb_p(MIXCOM_OFF , MIXCOM_BOARD_BASE(dev) + MIXCOM_IT_OFFSET); - udelay(1000); -} - -static void mixcom_off(struct net_device *dev) -{ - wr_hscx(dev, HSCX_CCR1, 0x0); -} - -static void mixcom_on(struct net_device *dev) -{ - struct comx_channel *ch = dev->priv; - - wr_hscx(dev, HSCX_CCR1, HSCX_PU | HSCX_ODS | HSCX_ITF); // power up, push-pull - wr_hscx(dev, HSCX_CCR2, HSCX_CIE /* | HSCX_RIE */ ); - wr_hscx(dev, HSCX_MODE, HSCX_TRANS | HSCX_ADM8 | HSCX_RAC | HSCX_RTS ); - wr_hscx(dev, HSCX_RLCR, HSCX_RC | 47); // 1504 bytes - wr_hscx(dev, HSCX_MASK, HSCX_RSC | HSCX_TIN ); - hscx_cmd(dev, HSCX_XRES | HSCX_RHR); - - if (ch->HW_set_clock) ch->HW_set_clock(dev); - -} - -static int MIXCOM_send_packet(struct net_device *dev, struct sk_buff *skb) -{ - struct comx_channel *ch = dev->priv; - struct mixcom_privdata *hw = ch->HW_privdata; - unsigned long flags; - - if (ch->debug_flags & DEBUG_HW_TX) { - comx_debug_bytes(dev, skb->data, skb->len, "MIXCOM_send_packet"); - } - - if (!(ch->line_status & LINE_UP)) { - return FRAME_DROPPED; - } - - if (skb->len > HSCX_MTU) { - ch->stats.tx_errors++; - return FRAME_ERROR; - } - - save_flags(flags); cli(); - - if (test_and_set_bit(0, &hw->txbusy)) { - printk(KERN_ERR "%s: transmitter called while busy... dropping frame (length %d)\n", dev->name, skb->len); - restore_flags(flags); - return FRAME_DROPPED; - } - - - hw->sending = skb; - hw->tx_ptr = 0; - hw->txbusy = 1; -// atomic_inc(&skb->users); // save it - hscx_fill_fifo(dev); - restore_flags(flags); - - ch->stats.tx_packets++; - ch->stats.tx_bytes += skb->len; - - if (ch->debug_flags & DEBUG_HW_TX) { - comx_debug(dev, "MIXCOM_send_packet was successful\n\n"); - } - - return FRAME_ACCEPTED; -} - -static inline void mixcom_receive_frame(struct net_device *dev) -{ - struct comx_channel *ch=dev->priv; - struct mixcom_privdata *hw=ch->HW_privdata; - register byte rsta; - register word length; - - rsta = rd_hscx(dev, HSCX_RSTA) & (HSCX_VFR | HSCX_RDO | - HSCX_CRC | HSCX_RAB); - length = ((rd_hscx(dev, HSCX_RBCH) & 0x0f) << 8) | - rd_hscx(dev, HSCX_RBCL); - - if ( length > hw->rx_ptr ) { - hscx_empty_fifo(dev, length - hw->rx_ptr); - } - - if (!(rsta & HSCX_VFR)) { - ch->stats.rx_length_errors++; - } - if (rsta & HSCX_RDO) { - ch->stats.rx_over_errors++; - } - if (!(rsta & HSCX_CRC)) { - ch->stats.rx_crc_errors++; - } - if (rsta & HSCX_RAB) { - ch->stats.rx_frame_errors++; - } - ch->stats.rx_packets++; - ch->stats.rx_bytes += length; - - if (rsta == (HSCX_VFR | HSCX_CRC) && hw->recving) { - skb_trim(hw->recving, hw->rx_ptr - 1); - if (ch->debug_flags & DEBUG_HW_RX) { - comx_debug_skb(dev, hw->recving, - "MIXCOM_interrupt receiving"); - } - hw->recving->dev = dev; - if (ch->LINE_rx) { - ch->LINE_rx(dev, hw->recving); - } - } - else if(hw->recving) { - kfree_skb(hw->recving); - } - hw->recving = NULL; - hw->rx_ptr = 0; -} - - -static inline void mixcom_extended_interrupt(struct net_device *dev) -{ - struct comx_channel *ch=dev->priv; - struct mixcom_privdata *hw=ch->HW_privdata; - register byte exir; - - exir = rd_hscx(dev, HSCX_EXIR) & (HSCX_XDU | HSCX_RFO | HSCX_CSC ); - - if (exir & HSCX_RFO) { - ch->stats.rx_over_errors++; - if (hw->rx_ptr) { - kfree_skb(hw->recving); - hw->recving = NULL; hw->rx_ptr = 0; - } - printk(KERN_ERR "MIXCOM: rx overrun\n"); - hscx_cmd(dev, HSCX_RHR); - } - - if (exir & HSCX_XDU) { // xmit underrun - ch->stats.tx_errors++; - ch->stats.tx_aborted_errors++; - if (hw->tx_ptr) { - kfree_skb(hw->sending); - hw->sending = NULL; - hw->tx_ptr = 0; - } - hscx_cmd(dev, HSCX_XRES); - clear_bit(0, &hw->txbusy); - if (ch->LINE_tx) { - ch->LINE_tx(dev); - } - printk(KERN_ERR "MIXCOM: tx underrun\n"); - } - - if (exir & HSCX_CSC) { - ch->stats.tx_carrier_errors++; - if ((rd_hscx(dev, HSCX_STAR) & HSCX_CTS) == 0) { // Vonal le - if (test_and_clear_bit(0, &ch->lineup_pending)) { - del_timer(&ch->lineup_timer); - } else if (ch->line_status & LINE_UP) { - ch->line_status &= ~LINE_UP; - if (ch->LINE_status) { - ch->LINE_status(dev,ch->line_status); - } - } - } - if (!(ch->line_status & LINE_UP) && (rd_hscx(dev, HSCX_STAR) & - HSCX_CTS)) { // Vonal fol - if (!test_and_set_bit(0,&ch->lineup_pending)) { - ch->lineup_timer.function = comx_lineup_func; - ch->lineup_timer.data = (unsigned long)dev; - ch->lineup_timer.expires = jiffies + HZ * - ch->lineup_delay; - add_timer(&ch->lineup_timer); - hscx_cmd(dev, HSCX_XRES); - clear_bit(0, &hw->txbusy); - if (hw->sending) { - kfree_skb(hw->sending); - } - hw->sending=NULL; - hw->tx_ptr = 0; - } - } - } -} - - -static irqreturn_t MIXCOM_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - unsigned long flags; - struct net_device *dev = (struct net_device *)dev_id; - struct comx_channel *ch, *twin_ch; - struct mixcom_privdata *hw, *twin_hw; - register unsigned char ista; - - if (dev==NULL) { - printk(KERN_ERR "comx_interrupt: irq %d for unknown device\n",irq); - return IRQ_NONE; - } - - ch = dev->priv; - hw = ch->HW_privdata; - - save_flags(flags); cli(); - - while((ista = (rd_hscx(dev, HSCX_ISTA) & (HSCX_RME | HSCX_RPF | - HSCX_XPR | HSCX_EXB | HSCX_EXA | HSCX_ICA)))) { - register byte ista2 = 0; - - if (ista & HSCX_RME) { - mixcom_receive_frame(dev); - } - if (ista & HSCX_RPF) { - hscx_empty_fifo(dev, 32); - } - if (ista & HSCX_XPR) { - if (hw->tx_ptr) { - hscx_fill_fifo(dev); - } else { - clear_bit(0, &hw->txbusy); - ch->LINE_tx(dev); - } - } - - if (ista & HSCX_EXB) { - mixcom_extended_interrupt(dev); - } - - if ((ista & HSCX_EXA) && ch->twin) { - mixcom_extended_interrupt(ch->twin); - } - - if ((ista & HSCX_ICA) && ch->twin && - (ista2 = rd_hscx(ch->twin, HSCX_ISTA) & - (HSCX_RME | HSCX_RPF | HSCX_XPR ))) { - if (ista2 & HSCX_RME) { - mixcom_receive_frame(ch->twin); - } - if (ista2 & HSCX_RPF) { - hscx_empty_fifo(ch->twin, 32); - } - if (ista2 & HSCX_XPR) { - twin_ch=ch->twin->priv; - twin_hw=twin_ch->HW_privdata; - if (twin_hw->tx_ptr) { - hscx_fill_fifo(ch->twin); - } else { - clear_bit(0, &twin_hw->txbusy); - ch->LINE_tx(ch->twin); - } - } - } - } - - restore_flags(flags); - return IRQ_HANDLED; -} - -static int MIXCOM_open(struct net_device *dev) -{ - struct comx_channel *ch = dev->priv; - struct mixcom_privdata *hw = ch->HW_privdata; - struct proc_dir_entry *procfile = ch->procdir->subdir; - unsigned long flags; - int ret = -ENODEV; - - if (!dev->base_addr || !dev->irq) - goto err_ret; - - - if(hw->channel==1) { - if(!TWIN(dev) || !(COMX_CHANNEL(TWIN(dev))->init_status & - IRQ_ALLOCATED)) { - printk(KERN_ERR "%s: channel 0 not yet initialized\n",dev->name); - ret = -EAGAIN; - goto err_ret; - } - } - - - /* Is our hw present at all ? Not checking for channel 0 if it is already - open */ - if(hw->channel!=0 || !(ch->init_status & IRQ_ALLOCATED)) { - if (!request_region(dev->base_addr, MIXCOM_IO_EXTENT, dev->name)) { - ret = -EAGAIN; - goto err_ret; - } - if (mixcom_probe(dev)) { - ret = -ENODEV; - goto err_release_region; - } - } - - if(hw->channel==0 && !(ch->init_status & IRQ_ALLOCATED)) { - if (request_irq(dev->irq, MIXCOM_interrupt, 0, - dev->name, (void *)dev)) { - printk(KERN_ERR "MIXCOM: unable to obtain irq %d\n", dev->irq); - ret = -EAGAIN; - goto err_release_region; - } - } - - save_flags(flags); cli(); - - if(hw->channel==0 && !(ch->init_status & IRQ_ALLOCATED)) { - ch->init_status|=IRQ_ALLOCATED; - mixcom_board_on(dev); - } - - mixcom_on(dev); - - - hw->status=inb(MIXCOM_BOARD_BASE(dev) + MIXCOM_STATUS_OFFSET); - if(hw->status != 0xff) { - printk(KERN_DEBUG "%s: board has status register, good\n", dev->name); - hw->card_has_status=1; - } - - hw->txbusy = 0; - ch->init_status |= HW_OPEN; - - if (rd_hscx(dev, HSCX_STAR) & HSCX_CTS) { - ch->line_status |= LINE_UP; - } else { - ch->line_status &= ~LINE_UP; - } - - restore_flags(flags); - - ch->LINE_status(dev, ch->line_status); - - for (; procfile ; procfile = procfile->next) { - if (strcmp(procfile->name, FILENAME_IO) == 0 || - strcmp(procfile->name, FILENAME_CHANNEL) == 0 || - strcmp(procfile->name, FILENAME_CLOCK) == 0 || - strcmp(procfile->name, FILENAME_IRQ) == 0) { - procfile->mode = S_IFREG | 0444; - } - } - - return 0; - -err_release_region: - release_region(dev->base_addr, MIXCOM_IO_EXTENT); -err_ret: - return ret; -} - -static int MIXCOM_close(struct net_device *dev) -{ - struct comx_channel *ch = dev->priv; - struct mixcom_privdata *hw = ch->HW_privdata; - struct proc_dir_entry *procfile = ch->procdir->subdir; - unsigned long flags; - - - save_flags(flags); cli(); - - mixcom_off(dev); - - /* This is channel 0, twin is not open, we can safely turn off everything */ - if(hw->channel==0 && (!(TWIN(dev)) || - !(COMX_CHANNEL(TWIN(dev))->init_status & HW_OPEN))) { - mixcom_board_off(dev); - free_irq(dev->irq, dev); - release_region(dev->base_addr, MIXCOM_IO_EXTENT); - ch->init_status &= ~IRQ_ALLOCATED; - } - - /* This is channel 1, channel 0 has already been shutdown, we can release - this one too */ - if(hw->channel==1 && !(COMX_CHANNEL(TWIN(dev))->init_status & HW_OPEN)) { - if(COMX_CHANNEL(TWIN(dev))->init_status & IRQ_ALLOCATED) { - mixcom_board_off(TWIN(dev)); - free_irq(TWIN(dev)->irq, TWIN(dev)); - release_region(TWIN(dev)->base_addr, MIXCOM_IO_EXTENT); - COMX_CHANNEL(TWIN(dev))->init_status &= ~IRQ_ALLOCATED; - } - } - - /* the ioports for channel 1 can be safely released */ - if(hw->channel==1) { - release_region(dev->base_addr, MIXCOM_IO_EXTENT); - } - - restore_flags(flags); - - /* If we don't hold any hardware open */ - if(!(ch->init_status & IRQ_ALLOCATED)) { - for (; procfile ; procfile = procfile->next) { - if (strcmp(procfile->name, FILENAME_IO) == 0 || - strcmp(procfile->name, FILENAME_CHANNEL) == 0 || - strcmp(procfile->name, FILENAME_CLOCK) == 0 || - strcmp(procfile->name, FILENAME_IRQ) == 0) { - procfile->mode = S_IFREG | 0644; - } - } - } - - /* channel 0 was only waiting for us to close channel 1 - close it completely */ - - if(hw->channel==1 && !(COMX_CHANNEL(TWIN(dev))->init_status & HW_OPEN)) { - for (procfile=COMX_CHANNEL(TWIN(dev))->procdir->subdir; - procfile ; procfile = procfile->next) { - if (strcmp(procfile->name, FILENAME_IO) == 0 || - strcmp(procfile->name, FILENAME_CHANNEL) == 0 || - strcmp(procfile->name, FILENAME_CLOCK) == 0 || - strcmp(procfile->name, FILENAME_IRQ) == 0) { - procfile->mode = S_IFREG | 0644; - } - } - } - - ch->init_status &= ~HW_OPEN; - return 0; -} - -static int MIXCOM_statistics(struct net_device *dev,char *page) -{ - struct comx_channel *ch = dev->priv; - // struct mixcom_privdata *hw = ch->HW_privdata; - int len = 0; - - if(ch->init_status && IRQ_ALLOCATED) { - len += sprintf(page + len, "Mixcom board: hardware open\n"); - } - - return len; -} - -static int MIXCOM_dump(struct net_device *dev) { - return 0; -} - -static int mixcom_read_proc(char *page, char **start, off_t off, int count, - int *eof, void *data) -{ - struct proc_dir_entry *file = (struct proc_dir_entry *)data; - struct net_device *dev = file->parent->data; - struct comx_channel *ch = dev->priv; - struct mixcom_privdata *hw = ch->HW_privdata; - int len = 0; - - if (strcmp(file->name, FILENAME_IO) == 0) { - len = sprintf(page, "0x%x\n", - (unsigned int)MIXCOM_BOARD_BASE(dev)); - } else if (strcmp(file->name, FILENAME_IRQ) == 0) { - len = sprintf(page, "%d\n", (unsigned int)dev->irq); - } else if (strcmp(file->name, FILENAME_CLOCK) == 0) { - if (hw->clock) len = sprintf(page, "%d\n", hw->clock); - else len = sprintf(page, "external\n"); - } else if (strcmp(file->name, FILENAME_CHANNEL) == 0) { - len = sprintf(page, "%01d\n", hw->channel); - } else if (strcmp(file->name, FILENAME_TWIN) == 0) { - if (ch->twin) { - len = sprintf(page, "%s\n",ch->twin->name); - } else { - len = sprintf(page, "none\n"); - } - } else { - printk(KERN_ERR "mixcom_read_proc: internal error, filename %s\n", file->name); - return -EBADF; - } - - if (off >= len) { - *eof = 1; - return 0; - } - *start = page + off; - if (count >= len - off) *eof = 1; - return min_t(int, count, len - off); -} - - -static struct net_device *mixcom_twin_check(struct net_device *dev) -{ - struct comx_channel *ch = dev->priv; - struct proc_dir_entry *procfile = ch->procdir->parent->subdir; - struct mixcom_privdata *hw = ch->HW_privdata; - - struct net_device *twin; - struct comx_channel *ch_twin; - struct mixcom_privdata *hw_twin; - - - for ( ; procfile ; procfile = procfile->next) { - if(!S_ISDIR(procfile->mode)) continue; - - twin = procfile->data; - ch_twin = twin->priv; - hw_twin = ch_twin->HW_privdata; - - - if (twin != dev && dev->irq && dev->base_addr && - dev->irq == twin->irq && - ch->hardware == ch_twin->hardware && - dev->base_addr == twin->base_addr + - (1-2*hw->channel)*MIXCOM_CHANNEL_OFFSET && - hw->channel == (1 - hw_twin->channel)) { - if (!TWIN(twin) || TWIN(twin)==dev) { - return twin; - } - } - } - return NULL; -} - - -static void setup_twin(struct net_device* dev) -{ - - if(TWIN(dev) && TWIN(TWIN(dev))) { - TWIN(TWIN(dev))=NULL; - } - if ((TWIN(dev) = mixcom_twin_check(dev)) != NULL) { - if (TWIN(TWIN(dev)) && TWIN(TWIN(dev)) != dev) { - TWIN(dev)=NULL; - } else { - TWIN(TWIN(dev))=dev; - } - } -} - -static int mixcom_write_proc(struct file *file, const char *buffer, - u_long count, void *data) -{ - struct proc_dir_entry *entry = (struct proc_dir_entry *)data; - struct net_device *dev = (struct net_device *)entry->parent->data; - struct comx_channel *ch = dev->priv; - struct mixcom_privdata *hw = ch->HW_privdata; - char *page; - int value; - - if (!(page = (char *)__get_free_page(GFP_KERNEL))) { - return -ENOMEM; - } - - if (copy_from_user(page, buffer, count = min_t(unsigned long, count, PAGE_SIZE))) { - free_page((unsigned long)page); - return -EFAULT; - } - if (*(page + count - 1) == '\n') { - *(page + count - 1) = 0; - } - - if (strcmp(entry->name, FILENAME_IO) == 0) { - value = simple_strtoul(page, NULL, 0); - if (value != 0x180 && value != 0x280 && value != 0x380) { - printk(KERN_ERR "MIXCOM: incorrect io address!\n"); - } else { - dev->base_addr = MIXCOM_DEV_BASE(value,hw->channel); - } - } else if (strcmp(entry->name, FILENAME_IRQ) == 0) { - value = simple_strtoul(page, NULL, 0); - if (value < 0 || value > 15 || mixcom_set_irq[value]==0xFF) { - printk(KERN_ERR "MIXCOM: incorrect irq value!\n"); - } else { - dev->irq = value; - } - } else if (strcmp(entry->name, FILENAME_CLOCK) == 0) { - if (strncmp("ext", page, 3) == 0) { - hw->clock = 0; - } else { - int kbps; - - kbps = simple_strtoul(page, NULL, 0); - if (!kbps) { - hw->clock = 0; - } else { - hw->clock = kbps; - } - if (hw->clock < 32 || hw->clock > 2000) { - hw->clock = 0; - printk(KERN_ERR "MIXCOM: invalid clock rate!\n"); - } - } - if (ch->init_status & HW_OPEN && ch->HW_set_clock) { - ch->HW_set_clock(dev); - } - } else if (strcmp(entry->name, FILENAME_CHANNEL) == 0) { - value = simple_strtoul(page, NULL, 0); - if (value > 2) { - printk(KERN_ERR "Invalid channel number\n"); - } else { - dev->base_addr+=(hw->channel - value) * MIXCOM_CHANNEL_OFFSET; - hw->channel = value; - } - } else { - printk(KERN_ERR "hw_read_proc: internal error, filename %s\n", - entry->name); - return -EBADF; - } - - setup_twin(dev); - - free_page((unsigned long)page); - return count; -} - -static int MIXCOM_init(struct net_device *dev) { - struct comx_channel *ch = dev->priv; - struct mixcom_privdata *hw; - struct proc_dir_entry *new_file; - - if ((ch->HW_privdata = kmalloc(sizeof(struct mixcom_privdata), - GFP_KERNEL)) == NULL) { - return -ENOMEM; - } - - memset(hw = ch->HW_privdata, 0, sizeof(struct mixcom_privdata)); - - if ((new_file = create_proc_entry(FILENAME_IO, S_IFREG | 0644, - ch->procdir)) == NULL) { - goto cleanup_HW_privdata; - } - new_file->data = (void *)new_file; - new_file->read_proc = &mixcom_read_proc; - new_file->write_proc = &mixcom_write_proc; - new_file->nlink = 1; - - if ((new_file = create_proc_entry(FILENAME_IRQ, S_IFREG | 0644, - ch->procdir)) == NULL) { - goto cleanup_filename_io; - } - new_file->data = (void *)new_file; - new_file->read_proc = &mixcom_read_proc; - new_file->write_proc = &mixcom_write_proc; - new_file->nlink = 1; - -#if 0 - if ((new_file = create_proc_entry(FILENAME_CLOCK, S_IFREG | 0644, - ch->procdir)) == NULL) { - return -EIO; - } - new_file->data = (void *)new_file; - new_file->read_proc = &mixcom_read_proc; - new_file->write_proc = &mixcom_write_proc; - new_file->nlink = 1; -#endif - - if ((new_file = create_proc_entry(FILENAME_CHANNEL, S_IFREG | 0644, - ch->procdir)) == NULL) { - goto cleanup_filename_irq; - } - new_file->data = (void *)new_file; - new_file->read_proc = &mixcom_read_proc; - new_file->write_proc = &mixcom_write_proc; - new_file->nlink = 1; - - if ((new_file = create_proc_entry(FILENAME_TWIN, S_IFREG | 0444, - ch->procdir)) == NULL) { - goto cleanup_filename_channel; - } - new_file->data = (void *)new_file; - new_file->read_proc = &mixcom_read_proc; - new_file->write_proc = &mixcom_write_proc; - new_file->nlink = 1; - - setup_twin(dev); - - /* Fill in ch_struct hw specific pointers */ - ch->HW_access_board = NULL; - ch->HW_release_board = NULL; - ch->HW_txe = MIXCOM_txe; - ch->HW_open = MIXCOM_open; - ch->HW_close = MIXCOM_close; - ch->HW_send_packet = MIXCOM_send_packet; - ch->HW_statistics = MIXCOM_statistics; - ch->HW_set_clock = NULL; - - dev->base_addr = MIXCOM_DEV_BASE(MIXCOM_DEFAULT_IO,0); - dev->irq = MIXCOM_DEFAULT_IRQ; - - MOD_INC_USE_COUNT; - return 0; -cleanup_filename_channel: - remove_proc_entry(FILENAME_CHANNEL, ch->procdir); -cleanup_filename_irq: - remove_proc_entry(FILENAME_IRQ, ch->procdir); -cleanup_filename_io: - remove_proc_entry(FILENAME_IO, ch->procdir); -cleanup_HW_privdata: - kfree(ch->HW_privdata); - return -EIO; -} - -static int MIXCOM_exit(struct net_device *dev) -{ - struct comx_channel *ch = dev->priv; - struct mixcom_privdata *hw = ch->HW_privdata; - - if(hw->channel==0 && TWIN(dev)) { - return -EBUSY; - } - - if(hw->channel==1 && TWIN(dev)) { - TWIN(TWIN(dev))=NULL; - } - - kfree(ch->HW_privdata); - remove_proc_entry(FILENAME_IO, ch->procdir); - remove_proc_entry(FILENAME_IRQ, ch->procdir); -#if 0 - remove_proc_entry(FILENAME_CLOCK, ch->procdir); -#endif - remove_proc_entry(FILENAME_CHANNEL, ch->procdir); - remove_proc_entry(FILENAME_TWIN, ch->procdir); - - MOD_DEC_USE_COUNT; - return 0; -} - -static struct comx_hardware mixcomhw = { - "mixcom", - VERSION, - MIXCOM_init, - MIXCOM_exit, - MIXCOM_dump, - NULL -}; - -static int __init comx_hw_mixcom_init(void) -{ - return comx_register_hardware(&mixcomhw); -} - -static void __exit comx_hw_mixcom_exit(void) -{ - comx_unregister_hardware("mixcom"); -} - -module_init(comx_hw_mixcom_init); -module_exit(comx_hw_mixcom_exit); diff --git a/drivers/net/wan/comx-hw-munich.c b/drivers/net/wan/comx-hw-munich.c deleted file mode 100644 index 195bc2d25..000000000 --- a/drivers/net/wan/comx-hw-munich.c +++ /dev/null @@ -1,2854 +0,0 @@ -/* - * Hardware-level driver for the SliceCOM board for Linux kernels 2.4.X - * - * Current maintainer / latest changes: Pasztor Szilard - * - * Original author: Bartok Istvan - * Based on skeleton by Tivadar Szemethy - * - * 0.51: - * - port for 2.4.x - * - clean up some code, make it more portable - * - busted direct hardware access through mapped memory - * - fix a possible race - * - prevent procfs buffer overflow - * - * 0.50: - * - support for the pcicom board, lots of rearrangements - * - handle modem status lines - * - * 0.50a: - * - fix for falc version 1.0 - * - * 0.50b: T&t - * - fix for bad localbus - */ - -#define VERSION "0.51" -#define VERSIONSTR "SliceCOM v" VERSION ", 2002/01/07\n" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#define COMX_NEW - -#ifndef COMX_NEW -#include "../include/comx.h" -#include "../include/munich32x.h" -#include "../include/falc-lh.h" -#else -#include "comx.h" -#include "munich32x.h" -#include "falc-lh.h" -#endif - -MODULE_AUTHOR("Bartok Istvan , Gergely Madarasz , Szilard Pasztor "); -MODULE_DESCRIPTION("Hardware-level driver for the SliceCOM and PciCOM (WelCOM) adapters"); -MODULE_LICENSE("GPL"); -/* - * TODO: az ilyenek a comxhw.h -ban szoktak lenni, idovel menjenek majd oda: - */ - -#define FILENAME_BOARDNUM "boardnum" /* /proc/comx/comx0.1/boardnum */ -#define FILENAME_TIMESLOTS "timeslots" /* /proc/comx/comx0.1/timeslots */ -#define FILENAME_FRAMING "framing" /* /proc/comx/comx0.1/framing */ -#define FILENAME_LINECODE "linecode" /* /proc/comx/comx0.1/linecode */ -#define FILENAME_CLOCK_SOURCE "clock_source" /* /proc/comx/comx0.1/clock_source */ -#define FILENAME_LOOPBACK "loopback" /* /proc/comx/comx0.1/loopback */ -#define FILENAME_REG "reg" /* /proc/comx/comx0.1/reg */ -#define FILENAME_LBIREG "lbireg" /* /proc/comx/comx0.1/lbireg */ - -#define SLICECOM_BOARDNUM_DEFAULT 0 - -#define SLICECOM_FRAMING_CRC4 1 -#define SLICECOM_FRAMING_NO_CRC4 2 -#define SLICECOM_FRAMING_DEFAULT SLICECOM_FRAMING_CRC4 - -#define SLICECOM_LINECODE_HDB3 1 -#define SLICECOM_LINECODE_AMI 2 -#define SLICECOM_LINECODE_DEFAULT SLICECOM_LINECODE_HDB3 - -#define SLICECOM_CLOCK_SOURCE_LINE 1 -#define SLICECOM_CLOCK_SOURCE_INTERNAL 2 -#define SLICECOM_CLOCK_SOURCE_DEFAULT SLICECOM_CLOCK_SOURCE_LINE - -#define SLICECOM_LOOPBACK_NONE 1 -#define SLICECOM_LOOPBACK_LOCAL 2 -#define SLICECOM_LOOPBACK_REMOTE 3 -#define SLICECOM_LOOPBACK_DEFAULT SLICECOM_LOOPBACK_NONE - -#define MUNICH_VIRT(addr) (void *)(&bar1[addr]) - -struct slicecom_stringtable -{ - char *name; - int value; -}; - -/* A convention: keep "default" the last not NULL when reading from /proc, - "error" is an indication that something went wrong, we have an undefined value */ - -struct slicecom_stringtable slicecom_framings[] = -{ - {"crc4", SLICECOM_FRAMING_CRC4}, - {"no-crc4", SLICECOM_FRAMING_NO_CRC4}, - {"default", SLICECOM_FRAMING_DEFAULT}, - {"error", 0} -}; - -struct slicecom_stringtable slicecom_linecodes[] = -{ - {"hdb3", SLICECOM_LINECODE_HDB3}, - {"ami", SLICECOM_LINECODE_AMI}, - {"default", SLICECOM_LINECODE_DEFAULT}, - {"error", 0} -}; - -struct slicecom_stringtable slicecom_clock_sources[] = -{ - {"line", SLICECOM_CLOCK_SOURCE_LINE}, - {"internal", SLICECOM_CLOCK_SOURCE_INTERNAL}, - {"default", SLICECOM_CLOCK_SOURCE_DEFAULT}, - {"error", 0} -}; - -struct slicecom_stringtable slicecom_loopbacks[] = -{ - {"none", SLICECOM_LOOPBACK_NONE}, - {"local", SLICECOM_LOOPBACK_LOCAL}, - {"remote", SLICECOM_LOOPBACK_REMOTE}, - {"default", SLICECOM_LOOPBACK_DEFAULT}, - {"error", 0} -}; - -/* - * Some tunable values... - * - * Note: when tuning values which change the length of text in - * /proc/comx/comx[n]/status, keep in mind that it must be shorter then - * PAGESIZE ! - */ - -#define MAX_BOARDS 4 /* ezzel 4 kartya lehet a gepben: 0..3 */ -#define RX_DESC_MAX 8 /* Rx ring size, must be >= 4 */ -#define TX_DESC_MAX 4 /* Tx ring size, must be >= 2 */ - /* a sokkal hosszabb Tx ring mar ronthatja a nem-FIFO packet */ - /* schedulerek (fair queueing, stb.) hatekonysagat. */ -#define MAX_WORK 10 /* TOD: update the info max. ennyi-1 esemenyt dolgoz fel egy interrupt hivasnal */ - -/* - * These are tunable too, but don't touch them without fully understanding what is happening - */ - -#define UDELAY 20 /* We wait UDELAY usecs with disabled interrupts before and */ - /* after each command to avoid writing into each other's */ - /* ccb->action_spec. A _send_packet nem var, mert azt az */ - /* _interrupt()-bol is meghivhatja a LINE_tx() */ - -/* - * Just to avoid warnings about implicit declarations: - */ - -static int MUNICH_close(struct net_device *dev); -static struct comx_hardware slicecomhw; -static struct comx_hardware pcicomhw; - -static unsigned long flags; -static spinlock_t mister_lock = SPIN_LOCK_UNLOCKED; - -typedef volatile struct /* Time Slot Assignment */ -{ - u32 rxfillmask:8, // ----------------------------+------+ - // | | - rxchannel:5, // ----------------------+---+ | | - rti:1, // ---------------------+| | | | - res2:2, // -------------------++|| | | | - // |||| | | | - txfillmask:8, // ----------+------+ |||| | | | - // | | |||| | | | - txchannel:5, // ----+---+ | | |||| | | | - tti:1, // ---+| | | | |||| | | | - res1:2; // -++|| | | | |||| | | | - // 3 2 1 - // 10987654 32109876 54321098 76543210 -} timeslot_spec_t; - -typedef volatile struct /* Receive Descriptor */ -{ - u32 zero1:16, no:13, hi:1, hold:1, zero2:1; - - u32 next; - u32 data; - - u32 zero3:8, status:8, bno:13, zero4:1, c:1, fe:1; -} rx_desc_t; - -typedef volatile struct /* Transmit Descriptor */ -{ - u32 fnum:11, csm:1, no13:1, zero1:2, v110:1, no:13, hi:1, hold:1, fe:1; - - u32 next; - u32 data; - -} tx_desc_t; - -typedef volatile struct /* Channel Specification */ -{ - u32 iftf:1, mode:2, fa:1, trv:2, crc:1, inv:1, cs:1, tflag:7, ra:1, ro:1, - th:1, ta:1, to:1, ti:1, ri:1, nitbs:1, fit:1, fir:1, re:1, te:1, ch:1, - ifc:1, sfe:1, fe2:1; - - u32 frda; - u32 ftda; - - u32 itbs:6, zero1:26; - -} channel_spec_t; - -typedef volatile struct /* Configuration Control Block */ -{ - u32 action_spec; - u32 reserved1; - u32 reserved2; - timeslot_spec_t timeslot_spec[32]; - channel_spec_t channel_spec[32]; - u32 current_rx_desc[32]; - u32 current_tx_desc[32]; - u32 csa; /* Control Start Address. CSA = *CCBA; CCB = *CSA */ - /* MUNICH does it like: CCB = *( *CCBA ) */ -} munich_ccb_t; - -typedef volatile struct /* Entry in the interrupt queue */ -{ - u32 all; -} munich_intq_t; - -#define MUNICH_INTQLEN 63 /* Rx/Tx Interrupt Queue Length - (not the real len, but the TIQL/RIQL value) */ -#define MUNICH_INTQMAX ( 16*(MUNICH_INTQLEN+1) ) /* Rx/Tx/Periph Interrupt Queue size in munich_intq_t's */ -#define MUNICH_INTQSIZE ( 4*MUNICH_INTQMAX ) /* Rx/Tx/Periph Interrupt Queue size in bytes */ - -#define MUNICH_PIQLEN 4 /* Peripheral Interrupt Queue Length. Unlike the RIQL/TIQL, */ -#define MUNICH_PIQMAX ( 4*MUNICH_PIQLEN ) /* PIQL register needs it like this */ -#define MUNICH_PIQSIZE ( 4*MUNICH_PIQMAX ) - -typedef volatile u32 vol_u32; /* TOD: ezek megszunnek ha atirom readw()/writew()-re - kész */ -typedef volatile u8 vol_u8; - -typedef volatile struct /* counters of E1-errors and errored seconds, see rfc2495 */ -{ - /* use here only unsigned ints, we depend on it when calculating the sum for the last N intervals */ - - unsigned line_code_violations, /* AMI: bipolar violations, HDB3: hdb3 violations */ - path_code_violations, /* FAS errors and CRC4 errors */ - e_bit_errors, /* E-Bit Errors (the remote side received from us with CRC4-error) */ - slip_secs, /* number of seconds with (receive) Controlled Slip(s) */ - fr_loss_secs, /* number of seconds an Out Of Frame defect was detected */ - line_err_secs, /* number of seconds with one or more Line Code Violations */ - degraded_mins, /* Degraded Minute - the estimated error rate is >1E-6, but <1E-3 */ - errored_secs, /* Errored Second - at least one of these happened: - - Path Code Violation - - Out Of Frame defect - - Slip - - receiving AIS - - not incremented during an Unavailable Second */ - bursty_err_secs, /* Bursty Errored Second: (rfc2495 says it does not apply to E1) - - Path Code Violations >1, but <320 - - not a Severely Errored Second - - no AIS - - not incremented during an Unavailabla Second */ - severely_err_secs, /* Severely Errored Second: - - CRC4: >=832 Path COde Violations || >0 Out Of Frame defects - - noCRC4: >=2048 Line Code Violations - - not incremented during an Unavailable Second */ - unavail_secs; /* number of Unavailable Seconds. Unavailable state is said after: - - 10 contiguous Severely Errored Seconds - - or RAI || AIS || LOF || LOS - - (any) loopback has been set */ - - /* - * we do not strictly comply to the rfc: we do not retroactively reduce errored_secs, - * bursty_err_secs, severely_err_secs when 'unavailable state' is reached - */ - -} e1_stats_t; - -typedef volatile struct /* ezek board-adatok, nem lehetnek a slicecom_privdata -ban */ -{ - int use_count; /* num. of interfaces using the board */ - int irq; /* a kartya irq-ja. belemasoljuk a dev->irq -kba is, de csak hogy */ - /* szebb legyen az ifconfig outputja */ - /* ha != 0, az azt jelenti hogy az az irq most nekunk sikeresen */ - /* le van foglalva */ - struct pci_dev *pci; /* a kartya PCI strukturaja. NULL, ha nincs kartya */ - u32 *bar1; /* pci->base_address[0] ioremap()-ed by munich_probe(), */ - /* on x86 can be used both as a bus or virtual address. */ - /* These are the Munich's registers */ - u8 *lbi; /* pci->base_address[1] ioremap()-ed by munich_probe(), */ - /* this is a 256-byte range, the start of the LBI on the board */ - munich_ccb_t *ccb; /* virtual address of CCB */ - munich_intq_t *tiq; /* Tx Interrupt Queue */ - munich_intq_t *riq; /* Rx Interrupt Queue */ - munich_intq_t *piq; /* Peripheral Interrupt Queue (FALC interrupts arrive here) */ - int tiq_ptr, /* A 'current' helyek a tiq/riq/piq -ban. */ - riq_ptr, /* amikor feldolgoztam az interruptokat, a legelso ures */ - piq_ptr; /* interrupt_information szora mutatnak. */ - struct net_device *twins[32]; /* MUNICH channel -> network interface assignment */ - - unsigned long lastcheck; /* When were the Rx rings last checked. Time in jiffies */ - - struct timer_list modemline_timer; - char isx21; - char lineup; - char framing; /* a beallitasok tarolasa */ - char linecode; - char clock_source; - char loopback; - - char devname[30]; /* what to show in /proc/interrupts */ - unsigned histogram[MAX_WORK]; /* number of processed events in the interrupt loop */ - unsigned stat_pri_races; /* number of special events, we try to handle them */ - unsigned stat_pti_races; - unsigned stat_pri_races_missed; /* when it can not be handled, because of MAX_WORK */ - unsigned stat_pti_races_missed; - -#define SLICECOM_BOARD_INTERVALS_SIZE 97 - e1_stats_t intervals[SLICECOM_BOARD_INTERVALS_SIZE]; /* E1 line statistics */ - unsigned current_interval; /* pointer to the current interval */ - unsigned elapsed_seconds; /* elapsed seconds from the start of the current interval */ - unsigned ses_seconds; /* counter of contiguous Severely Errored Seconds */ - unsigned is_unavailable; /* set to 1 after 10 contiguous Severely Errored Seconds */ - unsigned no_ses_seconds; /* contiguous Severely Error -free seconds in unavail state */ - - unsigned deg_elapsed_seconds; /* for counting the 'Degraded Mins' */ - unsigned deg_cumulated_errors; - - struct module *owner; /* pointer to our module to avoid module load races */ -} munich_board_t; - -struct slicecom_privdata -{ - int busy; /* transmitter busy - number of packets in the Tx ring */ - int channel; /* Munich logical channel ('channel-group' in Cisco) */ - unsigned boardnum; - u32 timeslots; /* i-th bit means i-th timeslot is our */ - - int tx_ring_hist[TX_DESC_MAX]; /* histogram: number of packets in Tx ring when _send_packet is called */ - - tx_desc_t tx_desc[TX_DESC_MAX]; /* the ring of Tx descriptors */ - u8 tx_data[TX_DESC_MAX][TXBUFFER_SIZE]; /* buffers for data to transmit */ - int tx_desc_ptr; /* hanyadik descriptornal tartunk a beirassal */ - /* ahol ez all, oda irtunk utoljara */ - - rx_desc_t rx_desc[RX_DESC_MAX]; /* the ring of Rx descriptors */ - u8 rx_data[RX_DESC_MAX][RXBUFFER_SIZE]; /* buffers for received data */ - int rx_desc_ptr; /* hanyadik descriptornal tartunk az olvasassal */ - - int rafutott; -}; - -static u32 reg, reg_ertek; /* why static: don't write stack trash into regs if strtoul() fails */ -static u32 lbireg; -static u8 lbireg_ertek; /* why static: don't write stack trash into regs if strtoul() fails */ - -static munich_board_t slicecom_boards[MAX_BOARDS]; -static munich_board_t pcicom_boards[MAX_BOARDS]; - -/* - * Reprogram Idle Channel Registers in the FALC - send special code in not used channels - * Should be called from the open and close, when the timeslot assignment changes - */ - -void rework_idle_channels(struct net_device *dev) -{ - struct comx_channel *ch = netdev_priv(dev); - struct slicecom_privdata *hw = ch->HW_privdata; - munich_board_t *board = slicecom_boards + hw->boardnum; - munich_ccb_t *ccb = board->ccb; - - u8 *lbi = board->lbi; - int i, j, tmp; - - - spin_lock_irqsave(&mister_lock, flags); - - for (i = 0; i < 4; i++) - { - tmp = 0xFF; - for (j = 0; j < 8; j++) - if (ccb->timeslot_spec[8 * i + j].tti == 0) tmp ^= (0x80 >> j); - writeb(tmp, lbi + 0x30 + i); - } - - spin_unlock_irqrestore(&mister_lock, flags); -} - -/* - * Set PCM framing - /proc/comx/comx0/framing - */ - -void slicecom_set_framing(int boardnum, int value) -{ - u8 *lbi = slicecom_boards[boardnum].lbi; - - spin_lock_irqsave(&mister_lock, flags); - - slicecom_boards[boardnum].framing = value; - switch (value) - { - case SLICECOM_FRAMING_CRC4: - writeb(readb(lbi + FMR1) | 8, lbi + FMR1); - writeb((readb(lbi + FMR2) & 0x3f) | 0x80, lbi + FMR2); - break; - case SLICECOM_FRAMING_NO_CRC4: - writeb(readb(lbi + FMR1) & 0xf7, lbi + FMR1); - writeb(readb(lbi + FMR2) & 0x3f, lbi + FMR2); - break; - default: - printk("slicecom: board %d: unhandled " FILENAME_FRAMING - " value %d\n", boardnum, value); - } - - spin_unlock_irqrestore(&mister_lock, flags); -} - -/* - * Set PCM linecode - /proc/comx/comx0/linecode - */ - -void slicecom_set_linecode(int boardnum, int value) -{ - u8 *lbi = slicecom_boards[boardnum].lbi; - - spin_lock_irqsave(&mister_lock, flags); - - slicecom_boards[boardnum].linecode = value; - switch (value) - { - case SLICECOM_LINECODE_HDB3: - writeb(readb(lbi + FMR0) | 0xf0, lbi + FMR0); - break; - case SLICECOM_LINECODE_AMI: - writeb((readb(lbi + FMR0) & 0x0f) | 0xa0, lbi + FMR0); - break; - default: - printk("slicecom: board %d: unhandled " FILENAME_LINECODE - " value %d\n", boardnum, value); - } - spin_unlock_irqrestore(&mister_lock, flags); -} - -/* - * Set PCM clock source - /proc/comx/comx0/clock_source - */ - -void slicecom_set_clock_source(int boardnum, int value) -{ - u8 *lbi = slicecom_boards[boardnum].lbi; - - spin_lock_irqsave(&mister_lock, flags); - - slicecom_boards[boardnum].clock_source = value; - switch (value) - { - case SLICECOM_CLOCK_SOURCE_LINE: - writeb(readb(lbi + LIM0) & ~1, lbi + LIM0); - break; - case SLICECOM_CLOCK_SOURCE_INTERNAL: - writeb(readb(lbi + LIM0) | 1, lbi + LIM0); - break; - default: - printk("slicecom: board %d: unhandled " FILENAME_CLOCK_SOURCE - " value %d\n", boardnum, value); - } - spin_unlock_irqrestore(&mister_lock, flags); -} - -/* - * Set loopbacks - /proc/comx/comx0/loopback - */ - -void slicecom_set_loopback(int boardnum, int value) -{ - u8 *lbi = slicecom_boards[boardnum].lbi; - - spin_lock_irqsave(&mister_lock, flags); - - slicecom_boards[boardnum].loopback = value; - switch (value) - { - case SLICECOM_LOOPBACK_NONE: - writeb(readb(lbi + LIM0) & ~2, lbi + LIM0); /* Local Loop OFF */ - writeb(readb(lbi + LIM1) & ~2, lbi + LIM1); /* Remote Loop OFF */ - break; - case SLICECOM_LOOPBACK_LOCAL: - writeb(readb(lbi + LIM1) & ~2, lbi + LIM1); /* Remote Loop OFF */ - writeb(readb(lbi + LIM0) | 2, lbi + LIM0); /* Local Loop ON */ - break; - case SLICECOM_LOOPBACK_REMOTE: - writeb(readb(lbi + LIM0) & ~2, lbi + LIM0); /* Local Loop OFF */ - writeb(readb(lbi + LIM1) | 2, lbi + LIM1); /* Remote Loop ON */ - break; - default: - printk("slicecom: board %d: unhandled " FILENAME_LOOPBACK - " value %d\n", boardnum, value); - } - spin_unlock_irqrestore(&mister_lock, flags); -} - -/* - * Update E1 line status LEDs on the adapter - */ - -void slicecom_update_leds(munich_board_t * board) -{ - u32 *bar1 = board->bar1; - u8 *lbi = board->lbi; - u8 frs0; - u32 leds; - int i; - - spin_lock_irqsave(&mister_lock, flags); - - leds = 0; - frs0 = readb(lbi + FRS0); /* FRS0 bits described on page 137 */ - - if (!(frs0 & 0xa0)) - { - leds |= 0x2000; /* Green LED: Input signal seems to be OK, no LOS, no LFA */ - if (frs0 & 0x10) - leds |= 0x8000; /* Red LED: Receiving Remote Alarm */ - } - writel(leds, MUNICH_VIRT(GPDATA)); - - if (leds == 0x2000 && !board->lineup) - { /* line up */ - board->lineup = 1; - for (i = 0; i < 32; i++) - { - if (board->twins[i] && (board->twins[i]->flags & IFF_RUNNING)) - { - struct comx_channel *ch = board->twins[i]->priv; - - if (!test_and_set_bit(0, &ch->lineup_pending)) - { - ch->lineup_timer.function = comx_lineup_func; - ch->lineup_timer.data = (unsigned long)board->twins[i]; - ch->lineup_timer.expires = jiffies + HZ * ch->lineup_delay; - add_timer(&ch->lineup_timer); - } - } - } - } - else if (leds != 0x2000 && board->lineup) - { /* line down */ - board->lineup = 0; - for (i = 0; i < 32; i++) - if (board->twins[i] && (board->twins[i]->flags & IFF_RUNNING)) - { - struct comx_channel *ch = board->twins[i]->priv; - - if (test_and_clear_bit(0, &ch->lineup_pending)) - del_timer(&ch->lineup_timer); - else if (ch->line_status & LINE_UP) - { - ch->line_status &= ~LINE_UP; - if (ch->LINE_status) - ch->LINE_status(board->twins[i], ch->line_status); - } - } - } - spin_unlock_irqrestore(&mister_lock, flags); -} - -/* - * This function gets called every second when the FALC issues the interrupt. - * Hardware counters contain error counts for last 1-second time interval. - * We add them to the global counters here. - * Read rfc2495 to understand this. - */ - -void slicecom_update_line_counters(munich_board_t * board) -{ - e1_stats_t *curr_int = &board->intervals[board->current_interval]; - - u8 *lbi = board->lbi; - - unsigned framing_errors, code_violations, path_code_violations, crc4_errors, - e_bit_errors; - unsigned slip_detected, /* this one has logical value, not the number of slips! */ - out_of_frame_defect, /* logical value */ - ais_defect, /* logical value */ - errored_sec, bursty_err_sec, severely_err_sec = 0, failure_sec; - u8 isr2, isr3, isr5, frs0; - - spin_lock_irqsave(&mister_lock, flags); - - isr2 = readb(lbi + ISR2); /* ISR0-5 described on page 156 */ - isr3 = readb(lbi + ISR3); - isr5 = readb(lbi + ISR5); - frs0 = readb(lbi + FRS0); /* FRS0 described on page 137 */ - - /* Error Events: */ - - code_violations = readb(lbi + CVCL) + (readb(lbi + CVCH) << 8); - framing_errors = readb(lbi + FECL) + (readb(lbi + FECH) << 8); - crc4_errors = readb(lbi + CEC1L) + (readb(lbi + CEC1H) << 8); - e_bit_errors = readb(lbi + EBCL) + (readb(lbi + EBCH) << 8); - slip_detected = isr3 & (ISR3_RSN | ISR3_RSP); - - path_code_violations = framing_errors + crc4_errors; - - curr_int->line_code_violations += code_violations; - curr_int->path_code_violations += path_code_violations; - curr_int->e_bit_errors += e_bit_errors; - - /* Performance Defects: */ - - /* there was an LFA in the last second, but maybe disappeared: */ - out_of_frame_defect = (isr2 & ISR2_LFA) || (frs0 & FRS0_LFA); - - /* there was an AIS in the last second, but maybe disappeared: */ - ais_defect = (isr2 & ISR2_AIS) || (frs0 & FRS0_AIS); - - /* Performance Parameters: */ - - if (out_of_frame_defect) - curr_int->fr_loss_secs++; - if (code_violations) - curr_int->line_err_secs++; - - errored_sec = ((board->framing == SLICECOM_FRAMING_NO_CRC4) && - (code_violations)) || path_code_violations || - out_of_frame_defect || slip_detected || ais_defect; - - bursty_err_sec = !out_of_frame_defect && !ais_defect && - (path_code_violations > 1) && (path_code_violations < 320); - - switch (board->framing) - { - case SLICECOM_FRAMING_CRC4: - severely_err_sec = out_of_frame_defect || - (path_code_violations >= 832); - break; - case SLICECOM_FRAMING_NO_CRC4: - severely_err_sec = (code_violations >= 2048); - break; - } - - /* - * failure_sec: true if there was a condition leading to a failure - * (and leading to unavailable state) in this second: - */ - - failure_sec = (isr2 & ISR2_RA) || (frs0 & FRS0_RRA) /* Remote/Far End/Distant Alarm Failure */ - || ais_defect || out_of_frame_defect /* AIS or LOF Failure */ - || (isr2 & ISR2_LOS) || (frs0 & FRS0_LOS) /* Loss Of Signal Failure */ - || (board->loopback != SLICECOM_LOOPBACK_NONE); /* Loopback has been set */ - - if (board->is_unavailable) - { - if (severely_err_sec) - board->no_ses_seconds = 0; - else - board->no_ses_seconds++; - - if ((board->no_ses_seconds >= 10) && !failure_sec) - { - board->is_unavailable = 0; - board->ses_seconds = 0; - board->no_ses_seconds = 0; - } - } - else - { - if (severely_err_sec) - board->ses_seconds++; - else - board->ses_seconds = 0; - - if ((board->ses_seconds >= 10) || failure_sec) - { - board->is_unavailable = 1; - board->ses_seconds = 0; - board->no_ses_seconds = 0; - } - } - - if (board->is_unavailable) - curr_int->unavail_secs++; - else - { - if (slip_detected) - curr_int->slip_secs++; - curr_int->errored_secs += errored_sec; - curr_int->bursty_err_secs += bursty_err_sec; - curr_int->severely_err_secs += severely_err_sec; - } - - /* the RFC does not say clearly which errors to count here, we try to count bit errors */ - - if (!board->is_unavailable && !severely_err_sec) - { - board->deg_cumulated_errors += code_violations; - board->deg_elapsed_seconds++; - if (board->deg_elapsed_seconds >= 60) - { - if (board->deg_cumulated_errors >= 123) - curr_int->degraded_mins++; - board->deg_cumulated_errors = 0; - board->deg_elapsed_seconds = 0; - } - - } - - board->elapsed_seconds++; - if (board->elapsed_seconds >= 900) - { - board->current_interval = - (board->current_interval + 1) % SLICECOM_BOARD_INTERVALS_SIZE; - memset((void *)&board->intervals[board->current_interval], 0, - sizeof(e1_stats_t)); - board->elapsed_seconds = 0; - } - - spin_unlock_irqrestore(&mister_lock, flags); -} - -static void pcicom_modemline(unsigned long b) -{ - munich_board_t *board = (munich_board_t *) b; - struct net_device *dev = board->twins[0]; - struct comx_channel *ch = netdev_priv(dev); - unsigned long regs; - - regs = readl((void *)(&board->bar1[GPDATA])); - if ((ch->line_status & LINE_UP) && (regs & 0x0800)) - { - ch->line_status &= ~LINE_UP; - board->lineup = 0; - if (ch->LINE_status) - { - ch->LINE_status(dev, ch->line_status); - } - } - - if (!(ch->line_status & LINE_UP) && !(regs & 0x0800)) - { - ch->line_status |= LINE_UP; - board->lineup = 1; - if (ch->LINE_status) - { - ch->LINE_status(dev, ch->line_status); - } - } - - mod_timer((struct timer_list *)&board->modemline_timer, jiffies + HZ); -} - -/* - * Is it possible to transmit ? - * Called (may be called) by the protocol layer - */ - -static int MUNICH_txe(struct net_device *dev) -{ - struct comx_channel *ch = netdev_priv(dev); - struct slicecom_privdata *hw = ch->HW_privdata; - - return (hw->busy < TX_DESC_MAX - 1); -} - -/* - * Hw probe function. Detects all the boards in the system, - * and fills up slicecom_boards[] and pcicom_boards[] - * Returns 0 on success. - * We do not disable interrupts! - */ -static int munich_probe(void) -{ - struct pci_dev *pci; - int boardnum; - int slicecom_boardnum; - int pcicom_boardnum; - u32 *bar1; - u8 *lbi; - munich_board_t *board; - - for (boardnum = 0; boardnum < MAX_BOARDS; boardnum++) - { - pcicom_boards[boardnum].pci = 0; - pcicom_boards[boardnum].bar1 = 0; - pcicom_boards[boardnum].lbi = 0; - slicecom_boards[boardnum].pci = 0; - slicecom_boards[boardnum].bar1 = 0; - slicecom_boards[boardnum].lbi = 0; - } - - pci = NULL; - board = NULL; - slicecom_boardnum = 0; - pcicom_boardnum = 0; - - for (boardnum = 0; - boardnum < MAX_BOARDS && (pci = pci_find_device(PCI_VENDOR_ID_SIEMENS, - PCI_DEVICE_ID_SIEMENS_MUNICH32X, pci)); boardnum++) - { - if (pci_enable_device(pci)) - continue; - - printk("munich_probe: munich chip found, IRQ %d\n", pci->irq); - - bar1 = ioremap_nocache(pci->resource[0].start, 0x100); - lbi = ioremap_nocache(pci->resource[1].start, 0x100); - - if (bar1 && lbi) - { - pci_write_config_dword(pci, MUNICH_PCI_PCIRES, 0xe0000); - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(1); - pci_write_config_dword(pci, MUNICH_PCI_PCIRES, 0); - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(1); - /* check the type of the card */ - writel(LREG0_MAGIC, MUNICH_VIRT(LREG0)); - writel(LREG1_MAGIC, MUNICH_VIRT(LREG1)); - writel(LREG2_MAGIC, MUNICH_VIRT(LREG2)); - writel(LREG3_MAGIC, MUNICH_VIRT(LREG3)); - writel(LREG4_MAGIC, MUNICH_VIRT(LREG4)); - writel(LREG5_MAGIC, MUNICH_VIRT(LREG5)); - writel(LCONF_MAGIC2,MUNICH_VIRT(LCONF)); /* enable the DMSM */ - - if ((readb(lbi + VSTR) == 0x13) || (readb(lbi + VSTR) == 0x10)) - { - board = slicecom_boards + slicecom_boardnum; - sprintf((char *)board->devname, "slicecom%d", - slicecom_boardnum); - board->isx21 = 0; - slicecom_boardnum++; - } - else if ((readb(lbi + VSTR) == 0x6) || (readb(lbi + GIS) == 0x6)) - { - board = pcicom_boards + pcicom_boardnum; - sprintf((char *)board->devname, "pcicom%d", pcicom_boardnum); - board->isx21 = 1; - pcicom_boardnum++; - } - if (board) - { - printk("munich_probe: %s board found\n", board->devname); - writel(LCONF_MAGIC1, MUNICH_VIRT(LCONF)); /* reset the DMSM */ - board->pci = pci; - board->bar1 = bar1; - board->lbi = lbi; - board->framing = SLICECOM_FRAMING_DEFAULT; - board->linecode = SLICECOM_LINECODE_DEFAULT; - board->clock_source = SLICECOM_CLOCK_SOURCE_DEFAULT; - board->loopback = SLICECOM_LOOPBACK_DEFAULT; - board->owner = THIS_MODULE; - } - else - { - printk("munich_probe: Board error, VSTR: %02X\n", - readb(lbi + VSTR)); - iounmap((void *)bar1); - iounmap((void *)lbi); - } - } - else - { - printk("munich_probe: ioremap() failed, not enabling this board!\n"); - /* .pci = NULL, so the MUNICH_open will not try to open it */ - if (bar1) iounmap((void *)bar1); - if (lbi) iounmap((void *)lbi); - } - } - - if (!pci && !boardnum) - { - printk("munich_probe: no PCI present!\n"); - return -ENODEV; - } - - if (pcicom_boardnum + slicecom_boardnum == 0) - { - printk - ("munich_probe: Couldn't find any munich board: vendor:device %x:%x not found\n", - PCI_VENDOR_ID_SIEMENS, PCI_DEVICE_ID_SIEMENS_MUNICH32X); - return -ENODEV; - } - - /* Found some */ - if (pcicom_boardnum) - printk("%d pcicom board(s) found.\n", pcicom_boardnum); - if (slicecom_boardnum) - printk("%d slicecom board(s) found.\n", slicecom_boardnum); - - return 0; -} - -/* - * Reset the hardware. Get called only from within this module if needed. - */ -#if 0 -static int slicecom_reset(struct net_device *dev) -{ - struct comx_channel *ch = netdev_priv(dev); - - printk("slicecom_reset: resetting the hardware\n"); - - /* Begin to reset the hardware */ - - if (ch->HW_set_clock) - ch->HW_set_clock(dev); - - /* And finish it */ - - return 0; -} -#endif - -/* - * Transmit a packet. - * Called by the protocol layer - * Return values: - * FRAME_ACCEPTED: frame is being transmited, transmitter is busy - * FRAME_QUEUED: frame is being transmitted, there's more room in - * the transmitter for additional packet(s) - * FRAME_ERROR: - * FRAME_DROPPED: there was some error - */ - -static int MUNICH_send_packet(struct net_device *dev, struct sk_buff *skb) -{ - struct comx_channel *ch = netdev_priv(dev); - struct slicecom_privdata *hw = ch->HW_privdata; - - /* Send it to the debug facility too if needed: */ - - if (ch->debug_flags & DEBUG_HW_TX) - comx_debug_bytes(dev, skb->data, skb->len, "MUNICH_send_packet"); - - /* If the line is inactive, don't accept: */ - - /* TODO: atgondolni hogy mi is legyen itt */ - /* if (!(ch->line_status & LINE_UP)) return FRAME_DROPPED; */ - - /* More check, to be sure: */ - - if (skb->len > TXBUFFER_SIZE) - { - ch->stats.tx_errors++; - kfree_skb(skb); - return FRAME_ERROR; - } - - /* Maybe you have to disable irq's while programming the hw: */ - - spin_lock_irqsave(&mister_lock, flags); - - /* And more check: */ - - if (hw->busy >= TX_DESC_MAX - 1) - { - printk(KERN_ERR - "%s: Transmitter called while busy... dropping frame, busy = %d\n", - dev->name, hw->busy); - spin_unlock_irqrestore(&mister_lock, flags); - kfree_skb(skb); - return FRAME_DROPPED; - } - - if (hw->busy >= 0) - hw->tx_ring_hist[hw->busy]++; - /* DELL: */ - else - printk("slicecom: %s: FATAL: busy = %d\n", dev->name, hw->busy); - -// /* DEL: */ -// printk("slicecom: %s: _send_packet called, busy = %d\n", dev->name, hw->busy ); - - /* Packet can go, update stats: */ - - ch->stats.tx_packets++; - ch->stats.tx_bytes += skb->len; - - /* Pass the packet to the HW: */ - /* Step forward with the transmit descriptors: */ - - hw->tx_desc_ptr = (hw->tx_desc_ptr + 1) % TX_DESC_MAX; - - memcpy(&(hw->tx_data[hw->tx_desc_ptr][0]), skb->data, skb->len); - hw->tx_desc[hw->tx_desc_ptr].no = skb->len; - - /* We don't issue any command, just step with the HOLD bit */ - - hw->tx_desc[hw->tx_desc_ptr].hold = 1; - hw->tx_desc[(hw->tx_desc_ptr + TX_DESC_MAX - 1) % TX_DESC_MAX].hold = 0; - -#ifdef COMX_NEW - dev_kfree_skb(skb); -#endif - /* csomag kerult a Tx ringbe: */ - - hw->busy++; - - /* Report it: */ - - if (ch->debug_flags & DEBUG_HW_TX) - comx_debug(dev, "%s: MUNICH_send_packet was successful\n\n", dev->name); - - if (hw->busy >= TX_DESC_MAX - 1) - { - spin_unlock_irqrestore(&mister_lock, flags); - return FRAME_ACCEPTED; - } - - spin_unlock_irqrestore(&mister_lock, flags); - - /* All done */ - - return FRAME_QUEUED; -} - -/* - * Interrupt handler routine. - * Called by the Linux kernel. - * BEWARE! The interrupts are enabled on the call! - */ -static irqreturn_t MUNICH_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - struct sk_buff *skb; - int length; - int rx_status; - int work; /* hany esemenyt kezeltem mar le */ - u32 *bar1; - u8 *lbi; - u32 stat, /* az esemenyek, amiket a ebben a loop korben le kell meg kezelni */ - race_stat = 0, /* race eseten ebben uzenek magamnak hogy mit kell meg lekezelni */ - ack; /* ezt fogom a vegen a STAT-ba irni, kiveszek belole 1-1 bitet ha */ - - /* az adott dolgot nem kell ack-olni mert volt vele munkam, es */ - /* legjobb ha visszaterek ide megegyszer */ - munich_intq_t int_info; - - struct net_device *dev; - struct comx_channel *ch; - struct slicecom_privdata *hw; - munich_board_t *board = (munich_board_t *) dev_id; - int channel; - - // , boardnum = (int)dev_id; - - // board = munich_boards + boardnum; - bar1 = board->bar1; - lbi = board->lbi; - - // Do not uncomment this under heavy load! :-> - // printk("MUNICH_interrupt: masked STAT=0x%08x, tiq=0x%08x, riq=0x%08x, piq=0x%08x\n", stat, board->tiq[0].all, board->riq[0].all, board->piq[0].all ); - - for (work = 0; (stat = (race_stat | (readl(MUNICH_VIRT(STAT)) & ~STAT_NOT_HANDLED_BY_INTERRUPT))) && (work < MAX_WORK - 1); work++) - { - ack = stat & (STAT_PRI | STAT_PTI | STAT_LBII); - - /* Handle the interrupt information in the Rx queue. We don't really trust */ - /* info from this queue, because it can be overflowed, so later check */ - /* every Rx ring for received packets. But there are some errors which can't */ - /* be counted from the Rx rings, so we parse it. */ - - int_info = board->riq[board->riq_ptr]; - if (int_info.all & 0xF0000000) /* ha ez nem 0, akkor itt interrupt_info van */ - { - ack &= ~STAT_PRI; /* don't ack the interrupt, we had some work to do */ - - channel = PCM_INT_CHANNEL(int_info.all); - dev = board->twins[channel]; - - if (dev == NULL) - { - printk - ("MUNICH_interrupt: got an Rx interrupt info for NULL device " - "%s.twins[%d], int_info = 0x%08x\n", board->devname, - channel, int_info.all); - goto go_for_next_interrupt; - } - - ch = netdev_priv(dev); - hw = (struct slicecom_privdata *)ch->HW_privdata; - - // printk("Rx STAT=0x%08x int_info=0x%08x rx_desc_ptr=%d rx_desc.status=0x%01x\n", - // stat, int_info.all, hw->rx_desc_ptr, hw->rx_desc[ hw->rx_desc_ptr ].status ); - - if (int_info.all & PCM_INT_HI) - printk("SliceCOM: %s: Host Initiated interrupt\n", dev->name); - if (int_info.all & PCM_INT_IFC) - printk("SliceCOM: %s: Idle/Flag Change\n", dev->name); - /* TOD: jo ez az Idle/Flag Change valamire? - azonnal latszik belole hogy mikor ad a masik oldal */ - /* TOD: ilyen IT most nem is jon, mert ki van maszkolva az interrupt, biztosan kell ez? */ - - if (int_info.all & PCM_INT_FO) - /* Internal buffer (RB) overrun */ - ch->stats.rx_over_errors++; /* TOD: Ez azt jelenti hogy a belso RB nem volt hozzaferheto, es ezert kihagyott valamit. De nem csak csomag lehetett, hanem esemeny, stb. is. lasd page 247. Ezzel a 'cat status'-hoz igazodok, de a netdevice.h szerint nem egyertelmu hogy ide ez kellene. Nem lehet hogy rx_missed ? */ - /* DE: nem gotozok sehova, elvileg jo igy */ - /* kesobb meg visszaterek az FO-ra, ha packet-FO volt. Keresd a "packet-FO"-t. */ - if (int_info.all & PCM_INT_FI) /* frame received, but we do not trust the int_info queue */ - if (int_info.all & PCM_INT_SF) - { /* Short Frame: rovidebb mint a CRC */ - /* "rovidebb mint CRC+2byte" vizsgalat a "CRC+2"-nel */ - ch->stats.rx_length_errors++; /* TOD: noveljem? ne noveljem? */ - goto go_for_next_interrupt; - } - - go_for_next_interrupt: /* One step in the interrupt queue */ - board->riq[board->riq_ptr].all = 0; /* megjelolom hogy itt meg nem jart a hw */ - board->riq_ptr = (board->riq_ptr + 1) % MUNICH_INTQMAX; - - } - - /* Check every Rx ring for incomed packets: */ - - for (channel = 0; channel < 32; channel++) - { - dev = board->twins[channel]; - - if (dev != NULL) - { - ch = netdev_priv(dev); - hw = (struct slicecom_privdata *)ch->HW_privdata; - - rx_status = hw->rx_desc[hw->rx_desc_ptr].status; - - if (!(rx_status & 0x80)) /* mar jart itt a hardver */ - { - ack &= ~STAT_PRI; /* Don't ack, we had some work */ - - /* Ez most egy kicsit zuros, mert itt mar nem latom az int_infot */ - if (rx_status & RX_STATUS_ROF) - ch->stats.rx_over_errors++; /* TOD: 'cat status'-hoz igazodok */ - - if (rx_status & RX_STATUS_RA) - /* Abort received or issued on channel */ - ch->stats.rx_frame_errors++; /* or HOLD bit in the descriptor */ - /* TOD: 'cat status'-hoz igazodok */ - - if (rx_status & RX_STATUS_LFD) - { /* Long Frame (longer then MFL in the MODE1) */ - ch->stats.rx_length_errors++; - goto go_for_next_frame; - } - - if (rx_status & RX_STATUS_NOB) - { /* Not n*8 bits long frame - frame alignment */ - ch->stats.rx_frame_errors++; /* ez viszont nem igazodik a 'cat status'-hoz */ - goto go_for_next_frame; - } - - if (rx_status & RX_STATUS_CRCO) - { /* CRC error */ - ch->stats.rx_crc_errors++; - goto go_for_next_frame; - } - - if (rx_status & RX_STATUS_SF) - { /* Short Frame: rovidebb mint CRC+2byte */ - ch->stats.rx_errors++; /* The HW does not set PCI_INT_ERR bit for this one, see page 246 */ - ch->stats.rx_length_errors++; - goto go_for_next_frame; - } - - if (rx_status != 0) - { - printk("SliceCOM: %s: unhandled rx_status: 0x%02x\n", - dev->name, rx_status); - goto go_for_next_frame; - } - - /* frame received without errors: */ - - length = hw->rx_desc[hw->rx_desc_ptr].bno; - ch->stats.rx_packets++; /* Count only 'good' packets */ - ch->stats.rx_bytes += length; - - /* Allocate a larger skb and reserve the heading for efficiency: */ - - if ((skb = dev_alloc_skb(length + 16)) == NULL) - { - ch->stats.rx_dropped++; - goto go_for_next_frame; - } - - /* Do bookkeeping: */ - - skb_reserve(skb, 16); - skb_put(skb, length); - skb->dev = dev; - - /* Now copy the data into the buffer: */ - - memcpy(skb->data, &(hw->rx_data[hw->rx_desc_ptr][0]), length); - - /* DEL: UGLY HACK!!!! */ - if (*((int *)skb->data) == 0x02000000 && - *(((int *)skb->data) + 1) == 0x3580008f) - { - printk("%s: swapping hack\n", dev->name); - *((int *)skb->data) = 0x3580008f; - *(((int *)skb->data) + 1) = 0x02000000; - } - - if (ch->debug_flags & DEBUG_HW_RX) - comx_debug_skb(dev, skb, "MUNICH_interrupt receiving"); - - /* Pass it to the protocol entity: */ - - ch->LINE_rx(dev, skb); - - go_for_next_frame: - /* DEL: rafutott-e a HOLD bitre -detektalas */ - { - if( ((rx_desc_t*)phys_to_virt(board->ccb->current_rx_desc[channel]))->hold - && ((rx_desc_t*)phys_to_virt(board->ccb->current_rx_desc[channel]))->status != 0xff) - hw->rafutott++; /* rafutott: hanyszor volt olyan hogy a current descriptoron HOLD bit volt, es a hw mar befejezte az irast (azaz a hw rafutott a HOLD bitre) */ - } - - // if( jiffies % 2 ) /* DELL: okozzunk egy kis Rx ring slipet :) */ - // { - /* Step forward with the receive descriptors: */ - /* if you change this, change the copy of it below too! Search for: "RxSlip" */ - hw->rx_desc[(hw->rx_desc_ptr + RX_DESC_MAX - 1) % RX_DESC_MAX].hold = 1; - hw->rx_desc[hw->rx_desc_ptr].status = 0xFF; /* megjelolom hogy itt meg nem jart a hw */ - hw->rx_desc[(hw->rx_desc_ptr + RX_DESC_MAX - 2) % RX_DESC_MAX].hold = 0; - hw->rx_desc_ptr = (hw->rx_desc_ptr + 1) % RX_DESC_MAX; - // } - } - } - } - - stat &= ~STAT_PRI; - -// } - -// if( stat & STAT_PTI ) /* TOD: primko megvalositas: mindig csak egy esemenyt dolgozok fel, */ - /* es nem torlom a STAT-ot, ezert ujra visszajon ide a rendszer. Amikor */ - /* jon interrupt, de nincs mit feldolgozni, akkor torlom a STAT-ot. */ - /* 'needs a rewrite', de elso megoldasnak jo lesz */ -// { - int_info = board->tiq[board->tiq_ptr]; - if (int_info.all & 0xF0000000) /* ha ez nem 0, akkor itt interrupt_info van */ - { - ack &= ~STAT_PTI; /* don't ack the interrupt, we had some work to do */ - - channel = PCM_INT_CHANNEL(int_info.all); - dev = board->twins[channel]; - - if (dev == NULL) - { - printk("MUNICH_interrupt: got a Tx interrupt for NULL device " - "%s.twins[%d], int_info = 0x%08x\n", - board->isx21 ? "pcicom" : "slicecom", channel, int_info.all); - goto go_for_next_tx_interrupt; - } - - ch = netdev_priv(dev); - hw = (struct slicecom_privdata *)ch->HW_privdata; - - // printk("Tx STAT=0x%08x int_info=0x%08x tiq_ptr=%d\n", stat, int_info.all, board->tiq_ptr ); - - if (int_info.all & PCM_INT_FE2) - { /* "Tx available" */ - /* do nothing */ - } - else if (int_info.all & PCM_INT_FO) - { /* Internal buffer (RB) overrun */ - ch->stats.rx_over_errors++; - } - else - { - printk("slicecom: %s: unhandled Tx int_info: 0x%08x\n", - dev->name, int_info.all); - } - - go_for_next_tx_interrupt: - board->tiq[board->tiq_ptr].all = 0; - board->tiq_ptr = (board->tiq_ptr + 1) % MUNICH_INTQMAX; - } - - /* Check every Tx ring for incoming packets: */ - - for (channel = 0; channel < 32; channel++) - { - dev = board->twins[channel]; - - if (dev != NULL) - { - int newbusy; - - ch = netdev_priv(dev); - hw = (struct slicecom_privdata *)ch->HW_privdata; - - /* We don't trust the "Tx available" info from the TIQ, but check */ - /* every ring if there is some free room */ - - if (ch->init_status && netif_running(dev)) - { - newbusy = ( TX_DESC_MAX + (& hw->tx_desc[ hw->tx_desc_ptr ]) - - (tx_desc_t*)phys_to_virt(board->ccb->current_tx_desc[ hw->channel ]) ) % TX_DESC_MAX; - - if(newbusy < 0) - { - printk("slicecom: %s: FATAL: fresly computed busy = %d, HW: 0x%p, SW: 0x%p\n", - dev->name, newbusy, - phys_to_virt(board->ccb->current_tx_desc[hw->channel]), - & hw->tx_desc[hw->tx_desc_ptr]); - } - - /* Fogyott valami a Tx ringbol? */ - - if (newbusy < hw->busy) - { - // ack &= ~STAT_PTI; /* Don't ack, we had some work */ - hw->busy = newbusy; - if (ch->LINE_tx) - ch->LINE_tx(dev); /* Report it to protocol driver */ - } - else if (newbusy > hw->busy) - printk("slicecom: %s: newbusy > hw->busy, this should not happen!\n", dev->name); - } - } - } - stat &= ~STAT_PTI; - - int_info = board->piq[board->piq_ptr]; - if (int_info.all & 0xF0000000) /* ha ez nem 0, akkor itt interrupt_info van */ - { - ack &= ~STAT_LBII; /* don't ack the interrupt, we had some work to do */ - - /* We do not really use (yet) the interrupt info from this queue, */ - - // printk("slicecom: %s: LBI Interrupt event: %08x\n", board->devname, int_info.all); - - if (!board->isx21) - { - slicecom_update_leds(board); - slicecom_update_line_counters(board); - } - - goto go_for_next_lbi_interrupt; /* To avoid warning about unused label */ - - go_for_next_lbi_interrupt: /* One step in the interrupt queue */ - board->piq[board->piq_ptr].all = 0; /* megjelolom hogy itt meg nem jart a hw */ - board->piq_ptr = (board->piq_ptr + 1) % MUNICH_PIQMAX; - } - stat &= ~STAT_LBII; - - writel(ack, MUNICH_VIRT(STAT)); - - if (stat & STAT_TSPA) - { - // printk("slicecom: %s: PCM TSP Asynchronous\n", board->devname); - writel(STAT_TSPA, MUNICH_VIRT(STAT)); - stat &= ~STAT_TSPA; - } - - if (stat & STAT_RSPA) - { - // printk("slicecom: %s: PCM RSP Asynchronous\n", board->devname); - writel(STAT_RSPA, MUNICH_VIRT(STAT)); - stat &= ~STAT_RSPA; - } - if (stat) - { - printk("MUNICH_interrupt: unhandled interrupt, STAT=0x%08x\n", - stat); - writel(stat, MUNICH_VIRT(STAT)); /* ha valamit megsem kezeltunk le, azert ack-ot kuldunk neki */ - } - - } - board->histogram[work]++; - - /* We can miss these if we reach the MAX_WORK */ - /* Count it to see how often it happens */ - - if (race_stat & STAT_PRI) - board->stat_pri_races_missed++; - if (race_stat & STAT_PTI) - board->stat_pti_races_missed++; - return IRQ_HANDLED; -} - -/* - * Hardware open routine. - * Called by comx (upper) layer when the user wants to bring up the interface - * with ifconfig. - * Initializes hardware, allocates resources etc. - * Returns 0 on OK, or standard error value on error. - */ - -static int MUNICH_open(struct net_device *dev) -{ - struct comx_channel *ch = netdev_priv(dev); - struct slicecom_privdata *hw = ch->HW_privdata; - struct proc_dir_entry *procfile = ch->procdir->subdir; - munich_board_t *board; - munich_ccb_t *ccb; - - u32 *bar1; - u8 *lbi; - u32 stat; - unsigned long flags, jiffs; - - int i, channel; - u32 timeslots = hw->timeslots; - - board = hw->boardnum + (ch->hardware == &pcicomhw ? pcicom_boards : slicecom_boards); - - bar1 = board->bar1; - lbi = board->lbi; - - /* TODO: a timeslotok ellenorzese kell majd ide .. hat, biztos? mar a write_proc-ban is - ellenorzom valamennyire. - if (!dev->io || !dev->irq) return -ENODEV; - */ - - if (!board->pci) - { - printk("MUNICH_open: no %s board with boardnum = %d\n", - ch->hardware->name, hw->boardnum); - return -ENODEV; - } - - spin_lock_irqsave(&mister_lock, flags); - /* lock the section to avoid race with multiple opens and make sure - that no interrupts get called while this lock is active */ - - if (board->use_count == 0) /* bring up the board if it was unused */ - /* if fails, frees allocated resources and returns. */ - /* TOD: is it safe? nem kellene resetelni a kartyat? */ - { - printk("MUNICH_open: %s: bringing up board\n", board->devname); - - /* Clean up the board's static struct if messed: */ - - for (i = 0; i < 32; i++) - board->twins[i] = NULL; - for (i = 0; i < MAX_WORK; i++) - board->histogram[i] = 0; - - board->lineup = 0; - - /* Allocate CCB: */ - board->ccb = kmalloc(sizeof(munich_ccb_t), GFP_KERNEL); - if (board->ccb == NULL) - { - spin_unlock_irqrestore(&mister_lock, flags); - return -ENOMEM; - } - memset((void *)board->ccb, 0, sizeof(munich_ccb_t)); - board->ccb->csa = virt_to_phys(board->ccb); - ccb = board->ccb; - for (i = 0; i < 32; i++) - { - ccb->timeslot_spec[i].tti = 1; - ccb->timeslot_spec[i].rti = 1; - } - - /* Interrupt queues: */ - - board->tiq = kmalloc(MUNICH_INTQSIZE, GFP_KERNEL); - if (board->tiq == NULL) - { - spin_unlock_irqrestore(&mister_lock, flags); - return -ENOMEM; - } - memset((void *)board->tiq, 0, MUNICH_INTQSIZE); - - board->riq = kmalloc(MUNICH_INTQSIZE, GFP_KERNEL); - if (board->riq == NULL) - { - spin_unlock_irqrestore(&mister_lock, flags); - return -ENOMEM; - } - memset((void *)board->riq, 0, MUNICH_INTQSIZE); - - board->piq = kmalloc(MUNICH_PIQSIZE, GFP_KERNEL); - if (board->piq == NULL) - { - spin_unlock_irqrestore(&mister_lock, flags); - return -ENOMEM; - } - memset((void *)board->piq, 0, MUNICH_PIQSIZE); - - board->tiq_ptr = 0; - board->riq_ptr = 0; - board->piq_ptr = 0; - - /* Request irq: */ - - board->irq = 0; - - /* (char*) cast to avoid warning about discarding volatile: */ - if (request_irq(board->pci->irq, MUNICH_interrupt, 0, - (char *)board->devname, (void *)board)) - { - printk("MUNICH_open: %s: unable to obtain irq %d\n", board->devname, - board->pci->irq); - /* TOD: free other resources (a sok malloc feljebb) */ - spin_unlock_irqrestore(&mister_lock, flags); - return -EAGAIN; - } - board->irq = board->pci->irq; /* csak akkor legyen != 0, ha tenyleg le van foglalva nekunk */ - - /* Programming device: */ - - /* Reset the board like a power-on: */ - /* TOD: - - It is not a real power-on: if a DMA transaction fails with master abort, the board - stays in half-dead state. - - It doesn't reset the FALC line driver */ - - pci_write_config_dword(board->pci, MUNICH_PCI_PCIRES, 0xe0000); - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(1); - pci_write_config_dword(board->pci, MUNICH_PCI_PCIRES, 0); - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(1); - - writel(virt_to_phys(&ccb->csa), MUNICH_VIRT(CCBA)); - writel(virt_to_phys( board->tiq ), MUNICH_VIRT(TIQBA)); - writel(MUNICH_INTQLEN, MUNICH_VIRT(TIQL)); - writel(virt_to_phys( board->riq ), MUNICH_VIRT(RIQBA)); - writel(MUNICH_INTQLEN, MUNICH_VIRT(RIQL)); - writel(virt_to_phys( board->piq ), MUNICH_VIRT(PIQBA)); - writel(MUNICH_PIQLEN, MUNICH_VIRT(PIQL)); - - /* Put the magic values into the registers: */ - - writel(MODE1_MAGIC, MUNICH_VIRT(MODE1)); - writel(MODE2_MAGIC, MUNICH_VIRT(MODE2)); - - writel(LREG0_MAGIC, MUNICH_VIRT(LREG0)); - writel(LREG1_MAGIC, MUNICH_VIRT(LREG1)); - writel(LREG2_MAGIC, MUNICH_VIRT(LREG2)); - writel(LREG3_MAGIC, MUNICH_VIRT(LREG3)); - writel(LREG4_MAGIC, MUNICH_VIRT(LREG4)); - writel(LREG5_MAGIC, MUNICH_VIRT(LREG5)); - - writel(LCONF_MAGIC1, MUNICH_VIRT(LCONF)); /* reset the DMSM */ - writel(LCONF_MAGIC2, MUNICH_VIRT(LCONF)); /* enable the DMSM */ - - writel(~0, MUNICH_VIRT(TXPOLL)); - writel(board->isx21 ? 0x1400 : 0xa000, MUNICH_VIRT(GPDIR)); - - if (readl(MUNICH_VIRT(STAT))) writel(readl(MUNICH_VIRT(STAT)), MUNICH_VIRT(STAT)); - - ccb->action_spec = CCB_ACTIONSPEC_RES | CCB_ACTIONSPEC_IA; - writel(CMD_ARPCM, MUNICH_VIRT(CMD)); /* Start the PCM core reset */ - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(1); - - stat = 0; /* Wait for the action to complete max. 1 second */ - jiffs = jiffies; - while (!((stat = readl(MUNICH_VIRT(STAT))) & (STAT_PCMA | STAT_PCMF)) && time_before(jiffies, jiffs + HZ)) - { - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(1); - } - - if (stat & STAT_PCMF) - { - printk(KERN_ERR - "MUNICH_open: %s: Initial ARPCM failed. STAT=0x%08x\n", - board->devname, stat); - writel(readl(MUNICH_VIRT(STAT)) & STAT_PCMF, MUNICH_VIRT(STAT)); - free_irq(board->irq, (void *)board); /* TOD: free other resources too *//* maybe shut down hw? */ - board->irq = 0; - spin_unlock_irqrestore(&mister_lock, flags); - return -EAGAIN; - } - else if (!(stat & STAT_PCMA)) - { - printk(KERN_ERR - "MUNICH_open: %s: Initial ARPCM timeout. STAT=0x%08x\n", - board->devname, stat); - free_irq(board->irq, (void *)board); /* TOD: free other resources too *//* maybe shut off the hw? */ - board->irq = 0; - spin_unlock_irqrestore(&mister_lock, flags); - return -EIO; - } - - writel(readl(MUNICH_VIRT(STAT)) & STAT_PCMA, MUNICH_VIRT(STAT)); /* Acknowledge */ - - if (board->isx21) writel(0, MUNICH_VIRT(GPDATA)); - - printk("MUNICH_open: %s: succesful HW-open took %ld jiffies\n", - board->devname, jiffies - jiffs); - - /* Set up the FALC hanging on the Local Bus: */ - - if (!board->isx21) - { - writeb(0x0e, lbi + FMR1); - writeb(0, lbi + LIM0); - writeb(0xb0, lbi + LIM1); /* TODO: input threshold */ - writeb(0xf7, lbi + XPM0); - writeb(0x02, lbi + XPM1); - writeb(0x00, lbi + XPM2); - writeb(0xf0, lbi + FMR0); - writeb(0x80, lbi + PCD); - writeb(0x80, lbi + PCR); - writeb(0x00, lbi + LIM2); - writeb(0x07, lbi + XC0); - writeb(0x3d, lbi + XC1); - writeb(0x05, lbi + RC0); - writeb(0x00, lbi + RC1); - writeb(0x83, lbi + FMR2); - writeb(0x9f, lbi + XSW); - writeb(0x0f, lbi + XSP); - writeb(0x00, lbi + TSWM); - writeb(0xe0, lbi + MODE); - writeb(0xff, lbi + IDLE); /* Idle Code to send in unused timeslots */ - writeb(0x83, lbi + IPC); /* interrupt query line mode: Push/pull output, active high */ - writeb(0xbf, lbi + IMR3); /* send an interrupt every second */ - - slicecom_set_framing(hw->boardnum, board->framing); - slicecom_set_linecode(hw->boardnum, board->linecode); - slicecom_set_clock_source(hw->boardnum, board->clock_source); - slicecom_set_loopback(hw->boardnum, board->loopback); - - memset((void *)board->intervals, 0, sizeof(board->intervals)); - board->current_interval = 0; - board->elapsed_seconds = 0; - board->ses_seconds = 0; - board->is_unavailable = 0; - board->no_ses_seconds = 0; - board->deg_elapsed_seconds = 0; - board->deg_cumulated_errors = 0; - } - - /* Enable the interrupts last */ - /* These interrupts will be enabled. We do not need the others. */ - - writel(readl(MUNICH_VIRT(IMASK)) & ~(STAT_PTI | STAT_PRI | STAT_LBII | STAT_TSPA | STAT_RSPA), MUNICH_VIRT(IMASK)); - } - - spin_unlock_irqrestore(&mister_lock, flags); - - dev->irq = board->irq; /* hogy szep legyen az ifconfig outputja */ - ccb = board->ccb; /* TODO: ez igy csunya egy kicsit hogy benn is meg kinn is beletoltom :( */ - - spin_lock_irqsave(&mister_lock, flags); - - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(1); - - /* Check if the selected timeslots aren't used already */ - - for (i = 0; i < 32; i++) - if (((1 << i) & timeslots) && !ccb->timeslot_spec[i].tti) - { - printk("MUNICH_open: %s: timeslot %d already used by %s\n", - dev->name, i, board->twins[ccb->timeslot_spec[i].txchannel]->name); - spin_unlock_irqrestore(&mister_lock, flags); - return -EBUSY; /* TODO: lehet hogy valami mas errno kellene? */ - } - - /* find a free channel: */ - /* TODO: ugly, rewrite it */ - - for (channel = 0; channel <= 32; channel++) - { - if (channel == 32) - { /* not found a free one */ - printk - ("MUNICH_open: %s: FATAL: can not find a free channel - this should not happen!\n", - dev->name); - spin_unlock_irqrestore(&mister_lock, flags); - return -ENODEV; - } - if (board->twins[channel] == NULL) - break; /* found the first free one */ - } - - board->lastcheck = jiffies; /* avoid checking uninitialized hardware channel */ - - /* Open the channel. If fails, calls MUNICH_close() to properly free resources and stop the HW */ - - hw->channel = channel; - board->twins[channel] = dev; - - board->use_count++; /* meg nem nyitottuk meg a csatornat, de a twins-ben - mar elfoglaltunk egyet, es ha a _close-t akarjuk hivni, akkor ez kell. */ - for (i = 0; i < 32; i++) - if ((1 << i) & timeslots) - { - ccb->timeslot_spec[i].tti = 0; - ccb->timeslot_spec[i].txchannel = channel; - ccb->timeslot_spec[i].txfillmask = ~0; - - ccb->timeslot_spec[i].rti = 0; - ccb->timeslot_spec[i].rxchannel = channel; - ccb->timeslot_spec[i].rxfillmask = ~0; - } - - if (!board->isx21) rework_idle_channels(dev); - - memset((void *)&(hw->tx_desc), 0, TX_DESC_MAX * sizeof(tx_desc_t)); - memset((void *)&(hw->rx_desc), 0, RX_DESC_MAX * sizeof(rx_desc_t)); - - for (i = 0; i < TX_DESC_MAX; i++) - { - hw->tx_desc[i].fe = 1; - hw->tx_desc[i].fnum = 2; - hw->tx_desc[i].data = virt_to_phys( & (hw->tx_data[i][0]) ); - hw->tx_desc[i].next = virt_to_phys( & (hw->tx_desc[ (i+1) % TX_DESC_MAX ]) ); - - } - hw->tx_desc_ptr = 0; /* we will send an initial packet so it is correct: "oda irtunk utoljara" */ - hw->busy = 0; - hw->tx_desc[hw->tx_desc_ptr].hold = 1; - hw->tx_desc[hw->tx_desc_ptr].no = 1; /* TOD: inkabb csak 0 hosszut kuldjunk ki az initkor? */ - - for (i = 0; i < RX_DESC_MAX; i++) - { - hw->rx_desc[i].no = RXBUFFER_SIZE; - hw->rx_desc[i].data = virt_to_phys(&(hw->rx_data[i][0])); - hw->rx_desc[i].next = virt_to_phys(&(hw->rx_desc[(i+1) % RX_DESC_MAX])); - hw->rx_desc[i].status = 0xFF; - } - hw->rx_desc_ptr = 0; - - hw->rx_desc[(hw->rx_desc_ptr + RX_DESC_MAX - 2) % RX_DESC_MAX].hold = 1; - - memset((void *)&ccb->channel_spec[channel], 0, sizeof(channel_spec_t)); - - ccb->channel_spec[channel].ti = 0; /* Transmit off */ - ccb->channel_spec[channel].to = 1; - ccb->channel_spec[channel].ta = 0; - - ccb->channel_spec[channel].th = 1; /* Transmit hold */ - - ccb->channel_spec[channel].ri = 0; /* Receive off */ - ccb->channel_spec[channel].ro = 1; - ccb->channel_spec[channel].ra = 0; - - ccb->channel_spec[channel].mode = 3; /* HDLC */ - - ccb->action_spec = CCB_ACTIONSPEC_IN | (channel << 8); - writel(CMD_ARPCM, MUNICH_VIRT(CMD)); - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(1); - - spin_unlock_irqrestore(&mister_lock, flags); - - stat = 0; - jiffs = jiffies; - while (!((stat = readl(MUNICH_VIRT(STAT))) & (STAT_PCMA | STAT_PCMF)) && time_before(jiffies, jiffs + HZ)) - { - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(1); - } - - if (stat & STAT_PCMF) - { - printk(KERN_ERR "MUNICH_open: %s: %s channel %d off failed\n", - dev->name, board->devname, channel); - writel(readl(MUNICH_VIRT(STAT)) & STAT_PCMF, MUNICH_VIRT(STAT)); - MUNICH_close(dev); - return -EAGAIN; - } - else if (!(stat & STAT_PCMA)) - { - printk(KERN_ERR "MUNICH_open: %s: %s channel %d off timeout\n", - dev->name, board->devname, channel); - MUNICH_close(dev); - return -EIO; - } - - writel(readl(MUNICH_VIRT(STAT)) & STAT_PCMA, MUNICH_VIRT(STAT)); - // printk("MUNICH_open: %s: succesful channel off took %ld jiffies\n", board->devname, jiffies-jiffs); - - spin_lock_irqsave(&mister_lock, flags); - - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(1); - - ccb->channel_spec[channel].ifc = 1; /* 1 .. 'Idle/Flag change' interrupt letiltva */ - ccb->channel_spec[channel].fit = 1; - ccb->channel_spec[channel].nitbs = 1; - ccb->channel_spec[channel].itbs = 2; - - /* TODOO: lehet hogy jo lenne igy, de utana kellene nezni hogy nem okoz-e fragmentaciot */ - // ccb->channel_spec[channel].itbs = 2 * number_of_timeslots; - // printk("open: %s: number_of_timeslots: %d\n", dev->name, number_of_timeslots); - - ccb->channel_spec[channel].mode = 3; /* HDLC */ - ccb->channel_spec[channel].ftda = virt_to_phys(&(hw->tx_desc)); - ccb->channel_spec[channel].frda = virt_to_phys(&(hw->rx_desc[0])); - - ccb->channel_spec[channel].ti = 1; /* Transmit init */ - ccb->channel_spec[channel].to = 0; - ccb->channel_spec[channel].ta = 1; - - ccb->channel_spec[channel].th = 0; - - ccb->channel_spec[channel].ri = 1; /* Receive init */ - ccb->channel_spec[channel].ro = 0; - ccb->channel_spec[channel].ra = 1; - - ccb->action_spec = CCB_ACTIONSPEC_ICO | (channel << 8); - writel(CMD_ARPCM, MUNICH_VIRT(CMD)); /* Start the channel init */ - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(1); - - spin_unlock_irqrestore(&mister_lock, flags); - - stat = 0; /* Wait for the action to complete max. 1 second */ - jiffs = jiffies; - while (!((stat = readl(MUNICH_VIRT(STAT))) & (STAT_PCMA | STAT_PCMF)) && time_before(jiffies, jiffs + HZ)) - { - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(1); - } - - if (stat & STAT_PCMF) - { - printk(KERN_ERR "MUNICH_open: %s: channel open ARPCM failed\n", - board->devname); - writel(readl(MUNICH_VIRT(STAT)) & STAT_PCMF, MUNICH_VIRT(STAT)); - MUNICH_close(dev); - return -EAGAIN; - } - else if (!(stat & STAT_PCMA)) - { - printk(KERN_ERR "MUNICH_open: %s: channel open ARPCM timeout\n", - board->devname); - MUNICH_close(dev); - return -EIO; - } - - writel(readl(MUNICH_VIRT(STAT)) & STAT_PCMA, MUNICH_VIRT(STAT)); - // printk("MUNICH_open: %s: succesful channel open took %ld jiffies\n", board->devname, jiffies-jiffs); - - spin_lock_irqsave(&mister_lock, flags); - - ccb->channel_spec[channel].nitbs = 0; /* once ITBS defined, these must be 0 */ - ccb->channel_spec[channel].itbs = 0; - - if (board->isx21) - { - init_timer(&board->modemline_timer); - board->modemline_timer.data = (unsigned long)board; - board->modemline_timer.function = pcicom_modemline; - board->modemline_timer.expires = jiffies + HZ; - add_timer((struct timer_list *)&board->modemline_timer); - } - - /* It is done. Declare that we're open: */ - hw->busy = 0; /* It may be 1 if the frame at Tx init already ended, but it is not */ - /* a real problem: we compute hw->busy on every interrupt */ - hw->rafutott = 0; - ch->init_status |= HW_OPEN; - - /* Initialize line state: */ - if (board->lineup) - ch->line_status |= LINE_UP; - else - ch->line_status &= ~LINE_UP; - - /* Remove w attribute from /proc files associated to hw parameters: - no write when the device is open */ - - for (; procfile; procfile = procfile->next) - if (strcmp(procfile->name, FILENAME_BOARDNUM) == 0 || - strcmp(procfile->name, FILENAME_TIMESLOTS) == 0) - procfile->mode = S_IFREG | 0444; - - spin_unlock_irqrestore(&mister_lock, flags); - - return 0; -} - -/* - * Hardware close routine. - * Called by comx (upper) layer when the user wants to bring down the interface - * with ifconfig. - * We also call it from MUNICH_open, if the open fails. - * Brings down hardware, frees resources, stops receiver - * Returns 0 on OK, or standard error value on error. - */ - -static int MUNICH_close(struct net_device *dev) -{ - struct comx_channel *ch = netdev_priv(dev); - struct slicecom_privdata *hw = ch->HW_privdata; - struct proc_dir_entry *procfile = ch->procdir->subdir; - munich_board_t *board; - munich_ccb_t *ccb; - - u32 *bar1; - u32 timeslots = hw->timeslots; - int stat, i, channel = hw->channel; - unsigned long jiffs; - - board = hw->boardnum + (ch->hardware == &pcicomhw ? pcicom_boards : slicecom_boards); - - ccb = board->ccb; - bar1 = board->bar1; - - if (board->isx21) - del_timer((struct timer_list *)&board->modemline_timer); - - spin_lock_irqsave(&mister_lock, flags); - - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(1); - - /* Disable receiver for the channel: */ - - for (i = 0; i < 32; i++) - if ((1 << i) & timeslots) - { - ccb->timeslot_spec[i].tti = 1; - ccb->timeslot_spec[i].txfillmask = 0; /* just to be double-sure :) */ - - ccb->timeslot_spec[i].rti = 1; - ccb->timeslot_spec[i].rxfillmask = 0; - } - - if (!board->isx21) rework_idle_channels(dev); - - ccb->channel_spec[channel].ti = 0; /* Receive off, Transmit off */ - ccb->channel_spec[channel].to = 1; - ccb->channel_spec[channel].ta = 0; - ccb->channel_spec[channel].th = 1; - - ccb->channel_spec[channel].ri = 0; - ccb->channel_spec[channel].ro = 1; - ccb->channel_spec[channel].ra = 0; - - board->twins[channel] = NULL; - - ccb->action_spec = CCB_ACTIONSPEC_IN | (channel << 8); - writel(CMD_ARPCM, MUNICH_VIRT(CMD)); - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(1); - - spin_unlock_irqrestore(&mister_lock, flags); - - stat = 0; - jiffs = jiffies; - while (!((stat = readl(MUNICH_VIRT(STAT))) & (STAT_PCMA | STAT_PCMF)) && time_before(jiffies, jiffs + HZ)) - { - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(1); - } - - if (stat & STAT_PCMF) - { - printk(KERN_ERR - "MUNICH_close: %s: FATAL: channel off ARPCM failed, not closing!\n", - dev->name); - writel(readl(MUNICH_VIRT(STAT)) & STAT_PCMF, MUNICH_VIRT(STAT)); - /* If we return success, the privdata (and the descriptor list) will be freed */ - return -EIO; - } - else if (!(stat & STAT_PCMA)) - printk(KERN_ERR "MUNICH_close: %s: channel off ARPCM timeout\n", - board->devname); - - writel(readl(MUNICH_VIRT(STAT)) & STAT_PCMA, MUNICH_VIRT(STAT)); - // printk("MUNICH_close: %s: channel off took %ld jiffies\n", board->devname, jiffies-jiffs); - - spin_lock_irqsave(&mister_lock, flags); - - if (board->use_count) board->use_count--; - - if (!board->use_count) /* we were the last user of the board */ - { - printk("MUNICH_close: bringing down board %s\n", board->devname); - - /* program down the board: */ - - writel(0x0000FF7F, MUNICH_VIRT(IMASK)); /* do not send any interrupts */ - writel(0, MUNICH_VIRT(CMD)); /* stop the timer if someone started it */ - writel(~0U, MUNICH_VIRT(STAT)); /* if an interrupt came between the cli()-sti(), quiet it */ - if (ch->hardware == &pcicomhw) - writel(0x1400, MUNICH_VIRT(GPDATA)); - - /* Put the board into 'reset' state: */ - pci_write_config_dword(board->pci, MUNICH_PCI_PCIRES, 0xe0000); - - /* Free irq and other resources: */ - if (board->irq) - free_irq(board->irq, (void *)board); /* Ha nem inicializalta magat, akkor meg nincs irq */ - board->irq = 0; - - /* Free CCB and the interrupt queues */ - if (board->ccb) kfree((void *)board->ccb); - if (board->tiq) kfree((void *)board->tiq); - if (board->riq) kfree((void *)board->riq); - if (board->piq) kfree((void *)board->piq); - board->ccb = NULL; - board->tiq = board->riq = board->piq = NULL; - } - - /* Enable setting of hw parameters */ - for (; procfile; procfile = procfile->next) - if (strcmp(procfile->name, FILENAME_BOARDNUM) == 0 || - strcmp(procfile->name, FILENAME_TIMESLOTS) == 0) - procfile->mode = S_IFREG | 0644; - - /* We're not open anymore */ - ch->init_status &= ~HW_OPEN; - - spin_unlock_irqrestore(&mister_lock, flags); - - return 0; -} - -/* - * Give (textual) status information. - * The text it returns will be a part of what appears when the user does a - * cat /proc/comx/comx[n]/status - * Don't write more than PAGESIZE. - * Return value: number of bytes written (length of the string, incl. 0) - */ - -static int MUNICH_minden(struct net_device *dev, char *page) -{ - struct comx_channel *ch = netdev_priv(dev); - struct slicecom_privdata *hw = ch->HW_privdata; - munich_board_t *board; - struct net_device *devp; - - u8 *lbi; - e1_stats_t *curr_int, *prev_int; - e1_stats_t last4, last96; /* sum of last 4, resp. last 96 intervals */ - unsigned *sump, /* running pointer for the sum data */ - *p; /* running pointer for the interval data */ - - int len = 0; - u8 frs0, frs1; - u8 fmr2; - int i, j; - u32 timeslots; - - board = hw->boardnum + (ch->hardware == &pcicomhw ? pcicom_boards : slicecom_boards); - - lbi = board->lbi; - curr_int = &board->intervals[board->current_interval]; - prev_int = - &board-> - intervals[(board->current_interval + SLICECOM_BOARD_INTERVALS_SIZE - - 1) % SLICECOM_BOARD_INTERVALS_SIZE]; - - if (!board->isx21) - { - frs0 = readb(lbi + FRS0); - fmr2 = readb(lbi + FMR2); - len += scnprintf(page + len, PAGE_SIZE - len, "Controller status:\n"); - if (frs0 == 0) - len += scnprintf(page + len, PAGE_SIZE - len, "\tNo alarms\n"); - else - { - if (frs0 & FRS0_LOS) - len += scnprintf(page + len, PAGE_SIZE - len, "\tLoss Of Signal\n"); - else - { - if (frs0 & FRS0_AIS) - len += scnprintf(page + len, PAGE_SIZE - len, - "\tAlarm Indication Signal\n"); - else - { - if (frs0 & FRS0_AUXP) - len += scnprintf(page + len, PAGE_SIZE - len, - "\tAuxiliary Pattern Indication\n"); - if (frs0 & FRS0_LFA) - len += scnprintf(page + len, PAGE_SIZE - len, - "\tLoss of Frame Alignment\n"); - else - { - if (frs0 & FRS0_RRA) - len += scnprintf(page + len, PAGE_SIZE - len, - "\tReceive Remote Alarm\n"); - - /* You can't set this framing with the /proc interface, but it */ - /* may be good to have here this alarm if you set it by hand: */ - - if ((board->framing == SLICECOM_FRAMING_CRC4) && - (frs0 & FRS0_LMFA)) - len += scnprintf(page + len, PAGE_SIZE - len, - "\tLoss of CRC4 Multiframe Alignment\n"); - - if (((fmr2 & 0xc0) == 0xc0) && (frs0 & FRS0_NMF)) - len += scnprintf(page + len, PAGE_SIZE - len, - "\tNo CRC4 Multiframe alignment Found after 400 msec\n"); - } - } - } - } - - frs1 = readb(lbi + FRS1); - if (FRS1_XLS & frs1) - len += scnprintf(page + len, PAGE_SIZE - len, - "\tTransmit Line Short\n"); - - /* debug Rx ring: DEL: - vagy meghagyni, de akkor legyen kicsit altalanosabb */ - } - - len += scnprintf(page + len, PAGE_SIZE - len, "Rx ring:\n"); - len += scnprintf(page + len, PAGE_SIZE - len, "\trafutott: %d\n", hw->rafutott); - len += scnprintf(page + len, PAGE_SIZE - len, - "\tlastcheck: %ld, jiffies: %ld\n", board->lastcheck, jiffies); - len += scnprintf(page + len, PAGE_SIZE - len, "\tbase: %08x\n", - (u32) virt_to_phys(&hw->rx_desc[0])); - len += scnprintf(page + len, PAGE_SIZE - len, "\trx_desc_ptr: %d\n", - hw->rx_desc_ptr); - len += scnprintf(page + len, PAGE_SIZE - len, "\trx_desc_ptr: %08x\n", - (u32) virt_to_phys(&hw->rx_desc[hw->rx_desc_ptr])); - len += scnprintf(page + len, PAGE_SIZE - len, "\thw_curr_ptr: %08x\n", - board->ccb->current_rx_desc[hw->channel]); - - for (i = 0; i < RX_DESC_MAX; i++) - len += scnprintf(page + len, PAGE_SIZE - len, "\t%08x %08x %08x %08x\n", - *((u32 *) & hw->rx_desc[i] + 0), - *((u32 *) & hw->rx_desc[i] + 1), - *((u32 *) & hw->rx_desc[i] + 2), - *((u32 *) & hw->rx_desc[i] + 3)); - - if (!board->isx21) - { - len += scnprintf(page + len, PAGE_SIZE - len, - "Interfaces using this board: (channel-group, interface, timeslots)\n"); - for (i = 0; i < 32; i++) - { - devp = board->twins[i]; - if (devp != NULL) - { - timeslots = - ((struct slicecom_privdata *)((struct comx_channel *)devp-> - priv)->HW_privdata)-> - timeslots; - len += scnprintf(page + len, PAGE_SIZE - len, "\t%2d %s: ", i, - devp->name); - for (j = 0; j < 32; j++) - if ((1 << j) & timeslots) - len += scnprintf(page + len, PAGE_SIZE - len, "%d ", j); - len += scnprintf(page + len, PAGE_SIZE - len, "\n"); - } - } - } - - len += scnprintf(page + len, PAGE_SIZE - len, "Interrupt work histogram:\n"); - for (i = 0; i < MAX_WORK; i++) - len += scnprintf(page + len, PAGE_SIZE - len, "hist[%2d]: %8u%c", i, - board->histogram[i], (i && - ((i + 1) % 4 == 0 || - i == MAX_WORK - 1)) ? '\n' : ' '); - - len += scnprintf(page + len, PAGE_SIZE - len, "Tx ring histogram:\n"); - for (i = 0; i < TX_DESC_MAX; i++) - len += scnprintf(page + len, PAGE_SIZE - len, "hist[%2d]: %8u%c", i, - hw->tx_ring_hist[i], (i && - ((i + 1) % 4 == 0 || - i == - TX_DESC_MAX - 1)) ? '\n' : ' '); - - if (!board->isx21) - { - - memset((void *)&last4, 0, sizeof(last4)); - memset((void *)&last96, 0, sizeof(last96)); - - /* Calculate the sum of last 4 intervals: */ - - for (i = 1; i <= 4; i++) - { - p = (unsigned *)&board->intervals[(board->current_interval + - SLICECOM_BOARD_INTERVALS_SIZE - - i) % SLICECOM_BOARD_INTERVALS_SIZE]; - sump = (unsigned *)&last4; - for (j = 0; j < (sizeof(e1_stats_t) / sizeof(unsigned)); j++) - sump[j] += p[j]; - } - - /* Calculate the sum of last 96 intervals: */ - - for (i = 1; i <= 96; i++) - { - p = (unsigned *)&board->intervals[(board->current_interval + - SLICECOM_BOARD_INTERVALS_SIZE - - i) % SLICECOM_BOARD_INTERVALS_SIZE]; - sump = (unsigned *)&last96; - for (j = 0; j < (sizeof(e1_stats_t) / sizeof(unsigned)); j++) - sump[j] += p[j]; - } - - len += scnprintf(page + len, PAGE_SIZE - len, - "Data in current interval (%d seconds elapsed):\n", - board->elapsed_seconds); - len += scnprintf(page + len, PAGE_SIZE - len, - " %d Line Code Violations, %d Path Code Violations, %d E-Bit Errors\n", - curr_int->line_code_violations, - curr_int->path_code_violations, curr_int->e_bit_errors); - len += scnprintf(page + len, PAGE_SIZE - len, - " %d Slip Secs, %d Fr Loss Secs, %d Line Err Secs, %d Degraded Mins\n", - curr_int->slip_secs, curr_int->fr_loss_secs, - curr_int->line_err_secs, curr_int->degraded_mins); - len += scnprintf(page + len, PAGE_SIZE - len, - " %d Errored Secs, %d Bursty Err Secs, %d Severely Err Secs, %d Unavail Secs\n", - curr_int->errored_secs, curr_int->bursty_err_secs, - curr_int->severely_err_secs, curr_int->unavail_secs); - - len += scnprintf(page + len, PAGE_SIZE - len, - "Data in Interval 1 (15 minutes):\n"); - len += scnprintf(page + len, PAGE_SIZE - len, - " %d Line Code Violations, %d Path Code Violations, %d E-Bit Errors\n", - prev_int->line_code_violations, - prev_int->path_code_violations, prev_int->e_bit_errors); - len += scnprintf(page + len, PAGE_SIZE - len, - " %d Slip Secs, %d Fr Loss Secs, %d Line Err Secs, %d Degraded Mins\n", - prev_int->slip_secs, prev_int->fr_loss_secs, - prev_int->line_err_secs, prev_int->degraded_mins); - len += scnprintf(page + len, PAGE_SIZE - len, - " %d Errored Secs, %d Bursty Err Secs, %d Severely Err Secs, %d Unavail Secs\n", - prev_int->errored_secs, prev_int->bursty_err_secs, - prev_int->severely_err_secs, prev_int->unavail_secs); - - len += scnprintf(page + len, PAGE_SIZE - len, - "Data in last 4 intervals (1 hour):\n"); - len += scnprintf(page + len, PAGE_SIZE - len, - " %d Line Code Violations, %d Path Code Violations, %d E-Bit Errors\n", - last4.line_code_violations, last4.path_code_violations, - last4.e_bit_errors); - len += scnprintf(page + len, PAGE_SIZE - len, - " %d Slip Secs, %d Fr Loss Secs, %d Line Err Secs, %d Degraded Mins\n", - last4.slip_secs, last4.fr_loss_secs, last4.line_err_secs, - last4.degraded_mins); - len += scnprintf(page + len, PAGE_SIZE - len, - " %d Errored Secs, %d Bursty Err Secs, %d Severely Err Secs, %d Unavail Secs\n", - last4.errored_secs, last4.bursty_err_secs, - last4.severely_err_secs, last4.unavail_secs); - - len += scnprintf(page + len, PAGE_SIZE - len, - "Data in last 96 intervals (24 hours):\n"); - len += scnprintf(page + len, PAGE_SIZE - len, - " %d Line Code Violations, %d Path Code Violations, %d E-Bit Errors\n", - last96.line_code_violations, last96.path_code_violations, - last96.e_bit_errors); - len += scnprintf(page + len, PAGE_SIZE - len, - " %d Slip Secs, %d Fr Loss Secs, %d Line Err Secs, %d Degraded Mins\n", - last96.slip_secs, last96.fr_loss_secs, - last96.line_err_secs, last96.degraded_mins); - len += scnprintf(page + len, PAGE_SIZE - len, - " %d Errored Secs, %d Bursty Err Secs, %d Severely Err Secs, %d Unavail Secs\n", - last96.errored_secs, last96.bursty_err_secs, - last96.severely_err_secs, last96.unavail_secs); - - } - -// len +=scnprintf( page + len, PAGE_SIZE - len, "Special events:\n" ); -// len +=scnprintf( page + len, PAGE_SIZE - len, "\tstat_pri/missed: %u / %u\n", board->stat_pri_races, board->stat_pri_races_missed ); -// len +=scnprintf( page + len, PAGE_SIZE - len, "\tstat_pti/missed: %u / %u\n", board->stat_pti_races, board->stat_pti_races_missed ); - return len; -} - -/* - * Memory dump function. Not used currently. - */ -static int BOARD_dump(struct net_device *dev) -{ - printk - ("BOARD_dump() requested. It is unimplemented, it should not be called\n"); - return (-1); -} - -/* - * /proc file read function for the files registered by this module. - * This function is called by the procfs implementation when a user - * wants to read from a file registered by this module. - * page is the workspace, start should point to the real start of data, - * off is the file offset, data points to the file's proc_dir_entry - * structure. - * Returns the number of bytes copied to the request buffer. - */ - -static int munich_read_proc(char *page, char **start, off_t off, int count, - int *eof, void *data) -{ - struct proc_dir_entry *file = (struct proc_dir_entry *)data; - struct net_device *dev = file->parent->data; - struct comx_channel *ch = netdev_priv(dev); - struct slicecom_privdata *hw = ch->HW_privdata; - munich_board_t *board; - - int len = 0, i; - u32 timeslots = hw->timeslots; - - board = hw->boardnum + (ch->hardware == &pcicomhw ? pcicom_boards : slicecom_boards); - - if (!strcmp(file->name, FILENAME_BOARDNUM)) - len = sprintf(page, "%d\n", hw->boardnum); - else if (!strcmp(file->name, FILENAME_TIMESLOTS)) - { - for (i = 0; i < 32; i++) - if ((1 << i) & timeslots) - len += scnprintf(page + len, PAGE_SIZE - len, "%d ", i); - len += scnprintf(page + len, PAGE_SIZE - len, "\n"); - } - else if (!strcmp(file->name, FILENAME_FRAMING)) - { - i = 0; - while (slicecom_framings[i].value && - slicecom_framings[i].value != board->framing) - i++; - len += scnprintf(page + len, PAGE_SIZE - len, "%s\n", - slicecom_framings[i].name); - } - else if (!strcmp(file->name, FILENAME_LINECODE)) - { - i = 0; - while (slicecom_linecodes[i].value && - slicecom_linecodes[i].value != board->linecode) - i++; - len += scnprintf(page + len, PAGE_SIZE - len, "%s\n", - slicecom_linecodes[i].name); - } - else if (!strcmp(file->name, FILENAME_CLOCK_SOURCE)) - { - i = 0; - while (slicecom_clock_sources[i].value && - slicecom_clock_sources[i].value != board->clock_source) - i++; - len += - scnprintf(page + len, PAGE_SIZE - len, "%s\n", - slicecom_clock_sources[i].name); - } - else if (!strcmp(file->name, FILENAME_LOOPBACK)) - { - i = 0; - while (slicecom_loopbacks[i].value && - slicecom_loopbacks[i].value != board->loopback) - i++; - len += scnprintf(page + len, PAGE_SIZE - len, "%s\n", - slicecom_loopbacks[i].name); - } - /* We set permissions to write-only for REG and LBIREG, but root can read them anyway: */ - else if (!strcmp(file->name, FILENAME_REG)) - { - len += scnprintf(page + len, PAGE_SIZE - len, - "%s: " FILENAME_REG ": write-only file\n", dev->name); - } - else if (!strcmp(file->name, FILENAME_LBIREG)) - { - len += scnprintf(page + len, PAGE_SIZE - len, - "%s: " FILENAME_LBIREG ": write-only file\n", dev->name); - } - else - { - printk("slicecom_read_proc: internal error, filename %s\n", file->name); - return -EBADF; - } - /* file handling administration: count eof status, offset, start address - and count: */ - - if (off >= len) - { - *eof = 1; - return 0; - } - - *start = page + off; - if (count >= len - off) - *eof = 1; - return min((off_t) count, (off_t) len - off); -} - -/* - * Write function for /proc files registered by us. - * See the comment on read function above. - * Beware! buffer is in userspace!!! - * Returns the number of bytes written - */ - -static int munich_write_proc(struct file *file, const char *buffer, - u_long count, void *data) -{ - struct proc_dir_entry *entry = (struct proc_dir_entry *)data; - struct net_device *dev = (struct net_device *)entry->parent->data; - struct comx_channel *ch = netdev_priv(dev); - struct slicecom_privdata *hw = ch->HW_privdata; - munich_board_t *board; - - unsigned long ts, tmp_boardnum; - - u32 tmp_timeslots = 0; - char *page, *p; - int i; - - board = hw->boardnum + (ch->hardware == &pcicomhw ? pcicom_boards : slicecom_boards); - - /* Paranoia checking: */ - - if (PDE(file->f_dentry->d_inode) != entry) - { - printk(KERN_ERR "munich_write_proc: file <-> data internal error\n"); - return -EIO; - } - - /* Request tmp buffer */ - if (!(page = (char *)__get_free_page(GFP_KERNEL))) - return -ENOMEM; - - /* Copy user data and cut trailing \n */ - if (copy_from_user(page, buffer, count = min(count, PAGE_SIZE))) { - free_page((unsigned long)page); - return -EFAULT; - } - if (*(page + count - 1) == '\n') - *(page + count - 1) = 0; - *(page + PAGE_SIZE - 1) = 0; - - if (!strcmp(entry->name, FILENAME_BOARDNUM)) - { - tmp_boardnum = simple_strtoul(page, NULL, 0); - if (0 <= tmp_boardnum && tmp_boardnum < MAX_BOARDS) - hw->boardnum = tmp_boardnum; - else - { - printk("%s: " FILENAME_BOARDNUM " range is 0...%d\n", dev->name, - MAX_BOARDS - 1); - free_page((unsigned long)page); - return -EINVAL; - } - } - else if (!strcmp(entry->name, FILENAME_TIMESLOTS)) - { - p = page; - while (*p) - { - if (isspace(*p)) - p++; - else - { - ts = simple_strtoul(p, &p, 10); /* base = 10: Don't read 09 as an octal number */ - /* ts = 0 ha nem tudta beolvasni a stringet, erre egy kicsit epitek itt: */ - if (0 <= ts && ts < 32) - { - tmp_timeslots |= (1 << ts); - } - else - { - printk("%s: " FILENAME_TIMESLOTS " range is 1...31\n", - dev->name); - free_page((unsigned long)page); - return -EINVAL; - } - } - } - hw->timeslots = tmp_timeslots; - } - else if (!strcmp(entry->name, FILENAME_FRAMING)) - { - i = 0; - while (slicecom_framings[i].value && - strncmp(slicecom_framings[i].name, page, - strlen(slicecom_framings[i].name))) - i++; - if (!slicecom_framings[i].value) - { - printk("slicecom: %s: Invalid " FILENAME_FRAMING " '%s'\n", - dev->name, page); - free_page((unsigned long)page); - return -EINVAL; - } - else - { /* - * If somebody says: - * echo >boardnum 0 - * echo >framing no-crc4 - * echo >boardnum 1 - * - when the framing was set, hw->boardnum was 0, so it would set the framing for board 0 - * Workaround: allow to set it only if interface is administrative UP - */ - if (netif_running(dev)) - slicecom_set_framing(hw->boardnum, slicecom_framings[i].value); - else - { - printk("%s: " FILENAME_FRAMING - " can not be set while the interface is DOWN\n", - dev->name); - free_page((unsigned long)page); - return -EINVAL; - } - } - } - else if (!strcmp(entry->name, FILENAME_LINECODE)) - { - i = 0; - while (slicecom_linecodes[i].value && - strncmp(slicecom_linecodes[i].name, page, - strlen(slicecom_linecodes[i].name))) - i++; - if (!slicecom_linecodes[i].value) - { - printk("slicecom: %s: Invalid " FILENAME_LINECODE " '%s'\n", - dev->name, page); - free_page((unsigned long)page); - return -EINVAL; - } - else - { /* - * Allow to set it only if interface is administrative UP, - * for the same reason as FILENAME_FRAMING - */ - if (netif_running(dev)) - slicecom_set_linecode(hw->boardnum, - slicecom_linecodes[i].value); - else - { - printk("%s: " FILENAME_LINECODE - " can not be set while the interface is DOWN\n", - dev->name); - free_page((unsigned long)page); - return -EINVAL; - } - } - } - else if (!strcmp(entry->name, FILENAME_CLOCK_SOURCE)) - { - i = 0; - while (slicecom_clock_sources[i].value && - strncmp(slicecom_clock_sources[i].name, page, - strlen(slicecom_clock_sources[i].name))) - i++; - if (!slicecom_clock_sources[i].value) - { - printk("%s: Invalid " FILENAME_CLOCK_SOURCE " '%s'\n", dev->name, - page); - free_page((unsigned long)page); - return -EINVAL; - } - else - { /* - * Allow to set it only if interface is administrative UP, - * for the same reason as FILENAME_FRAMING - */ - if (netif_running(dev)) - slicecom_set_clock_source(hw->boardnum, - slicecom_clock_sources[i].value); - else - { - printk("%s: " FILENAME_CLOCK_SOURCE - " can not be set while the interface is DOWN\n", - dev->name); - free_page((unsigned long)page); - return -EINVAL; - } - } - } - else if (!strcmp(entry->name, FILENAME_LOOPBACK)) - { - i = 0; - while (slicecom_loopbacks[i].value && - strncmp(slicecom_loopbacks[i].name, page, - strlen(slicecom_loopbacks[i].name))) - i++; - if (!slicecom_loopbacks[i].value) - { - printk("%s: Invalid " FILENAME_LOOPBACK " '%s'\n", dev->name, page); - free_page((unsigned long)page); - return -EINVAL; - } - else - { /* - * Allow to set it only if interface is administrative UP, - * for the same reason as FILENAME_FRAMING - */ - if (netif_running(dev)) - slicecom_set_loopback(hw->boardnum, - slicecom_loopbacks[i].value); - else - { - printk("%s: " FILENAME_LOOPBACK - " can not be set while the interface is DOWN\n", - dev->name); - free_page((unsigned long)page); - return -EINVAL; - } - } - } - else if (!strcmp(entry->name, FILENAME_REG)) - { /* DEL: 'reg' csak tmp */ - char *p; - u32 *bar1 = board->bar1; - - reg = simple_strtoul(page, &p, 0); - reg_ertek = simple_strtoul(p + 1, NULL, 0); - - if (reg < 0x100) - { - printk("reg(0x%02x) := 0x%08x jiff: %lu\n", reg, reg_ertek, jiffies); - writel(reg_ertek, MUNICH_VIRT(reg >> 2)); - } - else - { - printk("reg(0x%02x) is 0x%08x jiff: %lu\n", reg - 0x100, - readl(MUNICH_VIRT((reg - 0x100) >> 2)), jiffies); - } - } - else if (!strcmp(entry->name, FILENAME_LBIREG)) - { /* DEL: 'lbireg' csak tmp */ - char *p; - u8 *lbi = board->lbi; - - lbireg = simple_strtoul(page, &p, 0); - lbireg_ertek = simple_strtoul(p + 1, NULL, 0); - - if (lbireg < 0x100) - { - printk("lbireg(0x%02x) := 0x%02x jiff: %lu\n", lbireg, - lbireg_ertek, jiffies); - writeb(lbireg_ertek, lbi + lbireg); - } - else - printk("lbireg(0x%02x) is 0x%02x jiff: %lu\n", lbireg - 0x100, - readb(lbi + lbireg - 0x100), jiffies); - } - else - { - printk(KERN_ERR "munich_write_proc: internal error, filename %s\n", - entry->name); - free_page((unsigned long)page); - return -EBADF; - } - - /* Don't forget to free the workspace */ - free_page((unsigned long)page); - return count; -} - -/* - * Boardtype init function. - * Called by the comx (upper) layer, when you set boardtype. - * Allocates resources associated to using munich board for this device, - * initializes ch_struct pointers etc. - * Returns 0 on success and standard error codes on error. - */ - -static int init_escape(struct comx_channel *ch) -{ - kfree(ch->HW_privdata); - return -EIO; -} - -static int BOARD_init(struct net_device *dev) -{ - struct comx_channel *ch = netdev_priv(dev); - struct slicecom_privdata *hw; - struct proc_dir_entry *new_file; - - /* Alloc data for private structure */ - if ((ch->HW_privdata = - kmalloc(sizeof(struct slicecom_privdata), GFP_KERNEL)) == NULL) - return -ENOMEM; - - memset(hw = ch->HW_privdata, 0, sizeof(struct slicecom_privdata)); - - /* Register /proc files */ - if ((new_file = create_proc_entry(FILENAME_BOARDNUM, S_IFREG | 0644, - ch->procdir)) == NULL) - return init_escape(ch); - new_file->data = (void *)new_file; - new_file->read_proc = &munich_read_proc; - new_file->write_proc = &munich_write_proc; -// new_file->proc_iops = &comx_normal_inode_ops; - new_file->nlink = 1; - - if (ch->hardware == &slicecomhw) - { - if ((new_file = create_proc_entry(FILENAME_TIMESLOTS, S_IFREG | 0644, - ch->procdir)) == NULL) - return init_escape(ch); - new_file->data = (void *)new_file; - new_file->read_proc = &munich_read_proc; - new_file->write_proc = &munich_write_proc; -// new_file->proc_iops = &comx_normal_inode_ops; - new_file->nlink = 1; - - if ((new_file = create_proc_entry(FILENAME_FRAMING, S_IFREG | 0644, - ch->procdir)) == NULL) - return init_escape(ch); - new_file->data = (void *)new_file; - new_file->read_proc = &munich_read_proc; - new_file->write_proc = &munich_write_proc; -// new_file->proc_iops = &comx_normal_inode_ops; - new_file->nlink = 1; - - if ((new_file = create_proc_entry(FILENAME_LINECODE, S_IFREG | 0644, - ch->procdir)) == NULL) - return init_escape(ch); - new_file->data = (void *)new_file; - new_file->read_proc = &munich_read_proc; - new_file->write_proc = &munich_write_proc; -// new_file->proc_iops = &comx_normal_inode_ops; - new_file->nlink = 1; - - if ((new_file = create_proc_entry(FILENAME_CLOCK_SOURCE, S_IFREG | 0644, - ch->procdir)) == NULL) - return init_escape(ch); - new_file->data = (void *)new_file; - new_file->read_proc = &munich_read_proc; - new_file->write_proc = &munich_write_proc; -// new_file->proc_iops = &comx_normal_inode_ops; - new_file->nlink = 1; - - if ((new_file = create_proc_entry(FILENAME_LOOPBACK, S_IFREG | 0644, - ch->procdir)) == NULL) - return init_escape(ch); - new_file->data = (void *)new_file; - new_file->read_proc = &munich_read_proc; - new_file->write_proc = &munich_write_proc; -// new_file->proc_iops = &comx_normal_inode_ops; - new_file->nlink = 1; - } - - /* DEL: ez itt csak fejlesztesi celokra!! */ - if ((new_file = create_proc_entry(FILENAME_REG, S_IFREG | 0200, ch->procdir)) == NULL) - return init_escape(ch); - new_file->data = (void *)new_file; - new_file->read_proc = &munich_read_proc; - new_file->write_proc = &munich_write_proc; -// new_file->proc_iops = &comx_normal_inode_ops; - new_file->nlink = 1; - - /* DEL: ez itt csak fejlesztesi celokra!! */ - if ((new_file = create_proc_entry(FILENAME_LBIREG, S_IFREG | 0200, - ch->procdir)) == NULL) - return init_escape(ch); - new_file->data = (void *)new_file; - new_file->read_proc = &munich_read_proc; - new_file->write_proc = &munich_write_proc; -// new_file->proc_iops = &comx_normal_inode_ops; - new_file->nlink = 1; - - /* Fill in ch_struct hw specific pointers: */ - - ch->HW_txe = MUNICH_txe; - ch->HW_open = MUNICH_open; - ch->HW_close = MUNICH_close; - ch->HW_send_packet = MUNICH_send_packet; -#ifndef COMX_NEW - ch->HW_minden = MUNICH_minden; -#else - ch->HW_statistics = MUNICH_minden; -#endif - - hw->boardnum = SLICECOM_BOARDNUM_DEFAULT; - hw->timeslots = ch->hardware == &pcicomhw ? 0xffffffff : 2; - - /* O.K. Count one more user on this module */ - MOD_INC_USE_COUNT; - return 0; -} - -/* - * Boardtype exit function. - * Called by the comx (upper) layer, when you clear boardtype from munich. - * Frees resources associated to using munich board for this device, - * resets ch_struct pointers etc. - */ -static int BOARD_exit(struct net_device *dev) -{ - struct comx_channel *ch = netdev_priv(dev); - - /* Free private data area */ -// board = hw->boardnum + (ch->hardware == &pcicomhw ? pcicom_boards : slicecom_boards); - - kfree(ch->HW_privdata); - /* Remove /proc files */ - remove_proc_entry(FILENAME_BOARDNUM, ch->procdir); - if (ch->hardware == &slicecomhw) - { - remove_proc_entry(FILENAME_TIMESLOTS, ch->procdir); - remove_proc_entry(FILENAME_FRAMING, ch->procdir); - remove_proc_entry(FILENAME_LINECODE, ch->procdir); - remove_proc_entry(FILENAME_CLOCK_SOURCE, ch->procdir); - remove_proc_entry(FILENAME_LOOPBACK, ch->procdir); - } - remove_proc_entry(FILENAME_REG, ch->procdir); - remove_proc_entry(FILENAME_LBIREG, ch->procdir); - - /* Minus one user for the module accounting */ - MOD_DEC_USE_COUNT; - return 0; -} - -static struct comx_hardware slicecomhw = -{ - "slicecom", -#ifdef COMX_NEW - VERSION, -#endif - BOARD_init, - BOARD_exit, - BOARD_dump, - NULL -}; - -static struct comx_hardware pcicomhw = -{ - "pcicom", -#ifdef COMX_NEW - VERSION, -#endif - BOARD_init, - BOARD_exit, - BOARD_dump, - NULL -}; - -/* Module management */ - -static int __init init_mister(void) -{ - printk(VERSIONSTR); - comx_register_hardware(&slicecomhw); - comx_register_hardware(&pcicomhw); - return munich_probe(); -} - -static void __exit cleanup_mister(void) -{ - int i; - - comx_unregister_hardware("slicecom"); - comx_unregister_hardware("pcicom"); - - for (i = 0; i < MAX_BOARDS; i++) - { - if (slicecom_boards[i].bar1) - iounmap((void *)slicecom_boards[i].bar1); - if (slicecom_boards[i].lbi) - iounmap((void *)slicecom_boards[i].lbi); - if (pcicom_boards[i].bar1) - iounmap((void *)pcicom_boards[i].bar1); - if (pcicom_boards[i].lbi) - iounmap((void *)pcicom_boards[i].lbi); - } -} - -module_init(init_mister); -module_exit(cleanup_mister); diff --git a/drivers/net/wan/comx-proto-fr.c b/drivers/net/wan/comx-proto-fr.c deleted file mode 100644 index c9551366b..000000000 --- a/drivers/net/wan/comx-proto-fr.c +++ /dev/null @@ -1,1014 +0,0 @@ -/* - * Frame-relay protocol module for the COMX driver - * for Linux 2.2.X - * - * Original author: Tivadar Szemethy - * Maintainer: Gergely Madarasz - * - * Copyright (C) 1998-1999 ITConsult-Pro Co. - * - * Contributors: - * Arnaldo Carvalho de Melo (0.73) - * - * 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. - * - * Version 0.70 (99/06/14): - * - cleaned up the source code a bit - * - ported back to kernel, now works as builtin code - * - * Version 0.71 (99/06/25): - * - use skb priorities and queues for sending keepalive - * - use device queues for slave->master data transmit - * - set IFF_RUNNING only line protocol up - * - fixes on slave device flags - * - * Version 0.72 (99/07/09): - * - handle slave tbusy with master tbusy (should be fixed) - * - fix the keepalive timer addition/deletion - * - * Version 0.73 (00/08/15) - * - resource release on failure at fr_master_init and - * fr_slave_init - */ - -#define VERSION "0.73" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "comx.h" -#include "comxhw.h" - -MODULE_AUTHOR("Author: Tivadar Szemethy "); -MODULE_DESCRIPTION("Frame Relay protocol implementation for the COMX drivers" - "for Linux kernel 2.4.X"); -MODULE_LICENSE("GPL"); - -#define FRAD_UI 0x03 -#define NLPID_IP 0xcc -#define NLPID_Q933_LMI 0x08 -#define NLPID_CISCO_LMI 0x09 -#define Q933_ENQ 0x75 -#define Q933_LINESTAT 0x51 -#define Q933_COUNTERS 0x53 - -#define MAXALIVECNT 3 /* No. of failures */ - -struct fr_data { - u16 dlci; - struct net_device *master; - char keepa_pend; - char keepa_freq; - char keepalivecnt, keeploopcnt; - struct timer_list keepa_timer; - u8 local_cnt, remote_cnt; -}; - -static struct comx_protocol fr_master_protocol; -static struct comx_protocol fr_slave_protocol; -static struct comx_hardware fr_dlci; - -static void fr_keepalive_send(struct net_device *dev) -{ - struct comx_channel *ch = dev->priv; - struct fr_data *fr = ch->LINE_privdata; - struct sk_buff *skb; - u8 *fr_packet; - - skb=alloc_skb(dev->hard_header_len + 13, GFP_ATOMIC); - - if(skb==NULL) - return; - - skb_reserve(skb, dev->hard_header_len); - - fr_packet=(u8*)skb_put(skb, 13); - - fr_packet[0] = (fr->dlci & (1024 - 15)) >> 2; - fr_packet[1] = (fr->dlci & 15) << 4 | 1; // EA bit 1 - fr_packet[2] = FRAD_UI; - fr_packet[3] = NLPID_Q933_LMI; - fr_packet[4] = 0; - fr_packet[5] = Q933_ENQ; - fr_packet[6] = Q933_LINESTAT; - fr_packet[7] = 0x01; - fr_packet[8] = 0x01; - fr_packet[9] = Q933_COUNTERS; - fr_packet[10] = 0x02; - fr_packet[11] = ++fr->local_cnt; - fr_packet[12] = fr->remote_cnt; - - skb->dev = dev; - skb->priority = TC_PRIO_CONTROL; - dev_queue_xmit(skb); -} - -static void fr_keepalive_timerfun(unsigned long d) -{ - struct net_device *dev = (struct net_device *)d; - struct comx_channel *ch = dev->priv; - struct fr_data *fr = ch->LINE_privdata; - struct proc_dir_entry *dir = ch->procdir->parent->subdir; - struct comx_channel *sch; - struct fr_data *sfr; - struct net_device *sdev; - - if (ch->init_status & LINE_OPEN) { - if (fr->keepalivecnt == MAXALIVECNT) { - comx_status(dev, ch->line_status & ~PROTO_UP); - dev->flags &= ~IFF_RUNNING; - for (; dir ; dir = dir->next) { - if(!S_ISDIR(dir->mode)) { - continue; - } - - if ((sdev = dir->data) && (sch = sdev->priv) && - (sdev->type == ARPHRD_DLCI) && - (sfr = sch->LINE_privdata) - && (sfr->master == dev) && - (sdev->flags & IFF_UP)) { - sdev->flags &= ~IFF_RUNNING; - comx_status(sdev, - sch->line_status & ~PROTO_UP); - } - } - } - if (fr->keepalivecnt <= MAXALIVECNT) { - ++fr->keepalivecnt; - } - fr_keepalive_send(dev); - } - mod_timer(&fr->keepa_timer, jiffies + HZ * fr->keepa_freq); -} - -static void fr_rx_lmi(struct net_device *dev, struct sk_buff *skb, - u16 dlci, u8 nlpid) -{ - struct comx_channel *ch = dev->priv; - struct fr_data *fr = ch->LINE_privdata; - struct proc_dir_entry *dir = ch->procdir->parent->subdir; - struct comx_channel *sch; - struct fr_data *sfr; - struct net_device *sdev; - - if (dlci != fr->dlci || nlpid != NLPID_Q933_LMI || !fr->keepa_freq) { - return; - } - - fr->remote_cnt = skb->data[7]; - if (skb->data[8] == fr->local_cnt) { // keepalive UP! - fr->keepalivecnt = 0; - if ((ch->line_status & LINE_UP) && - !(ch->line_status & PROTO_UP)) { - comx_status(dev, ch->line_status |= PROTO_UP); - dev->flags |= IFF_RUNNING; - for (; dir ; dir = dir->next) { - if(!S_ISDIR(dir->mode)) { - continue; - } - - if ((sdev = dir->data) && (sch = sdev->priv) && - (sdev->type == ARPHRD_DLCI) && - (sfr = sch->LINE_privdata) - && (sfr->master == dev) && - (sdev->flags & IFF_UP)) { - sdev->flags |= IFF_RUNNING; - comx_status(sdev, - sch->line_status | PROTO_UP); - } - } - } - } -} - -static void fr_set_keepalive(struct net_device *dev, int keepa) -{ - struct comx_channel *ch = dev->priv; - struct fr_data *fr = ch->LINE_privdata; - - if (!keepa && fr->keepa_freq) { // switch off - fr->keepa_freq = 0; - if (ch->line_status & LINE_UP) { - comx_status(dev, ch->line_status | PROTO_UP); - dev->flags |= IFF_RUNNING; - del_timer(&fr->keepa_timer); - } - return; - } - - if (keepa) { // bekapcs - if(fr->keepa_freq && (ch->line_status & LINE_UP)) { - del_timer(&fr->keepa_timer); - } - fr->keepa_freq = keepa; - fr->local_cnt = fr->remote_cnt = 0; - init_timer(&fr->keepa_timer); - fr->keepa_timer.expires = jiffies + HZ; - fr->keepa_timer.function = fr_keepalive_timerfun; - fr->keepa_timer.data = (unsigned long)dev; - ch->line_status &= ~(PROTO_UP | PROTO_LOOP); - dev->flags &= ~IFF_RUNNING; - comx_status(dev, ch->line_status); - if(ch->line_status & LINE_UP) { - add_timer(&fr->keepa_timer); - } - } -} - -static void fr_rx(struct net_device *dev, struct sk_buff *skb) -{ - struct comx_channel *ch = dev->priv; - struct proc_dir_entry *dir = ch->procdir->parent->subdir; - struct net_device *sdev = dev; - struct comx_channel *sch; - struct fr_data *sfr; - u16 dlci; - u8 nlpid; - - if(skb->len <= 4 || skb->data[2] != FRAD_UI) { - kfree_skb(skb); - return; - } - - /* Itt majd ki kell talalni, melyik slave kapja a csomagot */ - dlci = ((skb->data[0] & 0xfc) << 2) | ((skb->data[1] & 0xf0) >> 4); - if ((nlpid = skb->data[3]) == 0) { // Optional padding - nlpid = skb->data[4]; - skb_pull(skb, 1); - } - skb_pull(skb, 4); /* DLCI and header throw away */ - - if (ch->debug_flags & DEBUG_COMX_DLCI) { - comx_debug(dev, "Frame received, DLCI: %d, NLPID: 0x%02x\n", - dlci, nlpid); - comx_debug_skb(dev, skb, "Contents"); - } - - /* Megkeressuk, kihez tartozik */ - for (; dir ; dir = dir->next) { - if(!S_ISDIR(dir->mode)) { - continue; - } - if ((sdev = dir->data) && (sch = sdev->priv) && - (sdev->type == ARPHRD_DLCI) && (sfr = sch->LINE_privdata) && - (sfr->master == dev) && (sfr->dlci == dlci)) { - skb->dev = sdev; - if (ch->debug_flags & DEBUG_COMX_DLCI) { - comx_debug(dev, "Passing it to %s\n",sdev->name); - } - if (dev != sdev) { - sch->stats.rx_packets++; - sch->stats.rx_bytes += skb->len; - } - break; - } - } - switch(nlpid) { - case NLPID_IP: - skb->protocol = htons(ETH_P_IP); - skb->mac.raw = skb->data; - comx_rx(sdev, skb); - break; - case NLPID_Q933_LMI: - fr_rx_lmi(dev, skb, dlci, nlpid); - default: - kfree_skb(skb); - break; - } -} - -static int fr_tx(struct net_device *dev) -{ - struct comx_channel *ch = dev->priv; - struct proc_dir_entry *dir = ch->procdir->parent->subdir; - struct net_device *sdev; - struct comx_channel *sch; - struct fr_data *sfr; - int cnt = 1; - - /* Ha minden igaz, 2 helyen fog allni a tbusy: a masternel, - es annal a slave-nel aki eppen kuldott. - Egy helyen akkor all, ha a master kuldott. - Ez megint jo lesz majd, ha utemezni akarunk */ - - /* This should be fixed, the slave tbusy should be set when - the masters queue is full and reset when not */ - - for (; dir ; dir = dir->next) { - if(!S_ISDIR(dir->mode)) { - continue; - } - if ((sdev = dir->data) && (sch = sdev->priv) && - (sdev->type == ARPHRD_DLCI) && (sfr = sch->LINE_privdata) && - (sfr->master == dev) && (netif_queue_stopped(sdev))) { - netif_wake_queue(sdev); - cnt++; - } - } - - netif_wake_queue(dev); - return 0; -} - -static void fr_status(struct net_device *dev, unsigned short status) -{ - struct comx_channel *ch = dev->priv; - struct fr_data *fr = ch->LINE_privdata; - struct proc_dir_entry *dir = ch->procdir->parent->subdir; - struct net_device *sdev; - struct comx_channel *sch; - struct fr_data *sfr; - - if (status & LINE_UP) { - if (!fr->keepa_freq) { - status |= PROTO_UP; - } - } else { - status &= ~(PROTO_UP | PROTO_LOOP); - } - - if (dev == fr->master && fr->keepa_freq) { - if (status & LINE_UP) { - fr->keepa_timer.expires = jiffies + HZ; - add_timer(&fr->keepa_timer); - fr->keepalivecnt = MAXALIVECNT + 1; - fr->keeploopcnt = 0; - } else { - del_timer(&fr->keepa_timer); - } - } - - /* Itt a status valtozast vegig kell vinni az osszes slave-n */ - for (; dir ; dir = dir->next) { - if(!S_ISDIR(dir->mode)) { - continue; - } - - if ((sdev = dir->data) && (sch = sdev->priv) && - (sdev->type == ARPHRD_FRAD || sdev->type == ARPHRD_DLCI) && - (sfr = sch->LINE_privdata) && (sfr->master == dev)) { - if(status & LINE_UP) { - netif_wake_queue(sdev); - } - comx_status(sdev, status); - if(status & (PROTO_UP | PROTO_LOOP)) { - dev->flags |= IFF_RUNNING; - } else { - dev->flags &= ~IFF_RUNNING; - } - } - } -} - -static int fr_open(struct net_device *dev) -{ - struct comx_channel *ch = dev->priv; - struct fr_data *fr = ch->LINE_privdata; - struct proc_dir_entry *comxdir = ch->procdir; - struct comx_channel *mch; - - if (!(ch->init_status & HW_OPEN)) { - return -ENODEV; - } - - if ((ch->hardware == &fr_dlci && ch->protocol != &fr_slave_protocol) || - (ch->protocol == &fr_slave_protocol && ch->hardware != &fr_dlci)) { - printk(KERN_ERR "Trying to open an improperly set FR interface, giving up\n"); - return -EINVAL; - } - - if (!fr->master) { - return -ENODEV; - } - mch = fr->master->priv; - if (fr->master != dev && (!(mch->init_status & LINE_OPEN) - || (mch->protocol != &fr_master_protocol))) { - printk(KERN_ERR "Master %s is inactive, or incorrectly set up, " - "unable to open %s\n", fr->master->name, dev->name); - return -ENODEV; - } - - ch->init_status |= LINE_OPEN; - ch->line_status &= ~(PROTO_UP | PROTO_LOOP); - dev->flags &= ~IFF_RUNNING; - - if (fr->master == dev) { - if (fr->keepa_freq) { - fr->keepa_timer.function = fr_keepalive_timerfun; - fr->keepa_timer.data = (unsigned long)dev; - add_timer(&fr->keepa_timer); - } else { - if (ch->line_status & LINE_UP) { - ch->line_status |= PROTO_UP; - dev->flags |= IFF_RUNNING; - } - } - } else { - ch->line_status = mch->line_status; - if(fr->master->flags & IFF_RUNNING) { - dev->flags |= IFF_RUNNING; - } - } - - for (; comxdir ; comxdir = comxdir->next) { - if (strcmp(comxdir->name, FILENAME_DLCI) == 0 || - strcmp(comxdir->name, FILENAME_MASTER) == 0 || - strcmp(comxdir->name, FILENAME_KEEPALIVE) == 0) { - comxdir->mode = S_IFREG | 0444; - } - } -// comx_status(dev, ch->line_status); - return 0; -} - -static int fr_close(struct net_device *dev) -{ - struct comx_channel *ch = dev->priv; - struct fr_data *fr = ch->LINE_privdata; - struct proc_dir_entry *comxdir = ch->procdir; - - if (fr->master == dev) { // Ha master - struct proc_dir_entry *dir = ch->procdir->parent->subdir; - struct net_device *sdev = dev; - struct comx_channel *sch; - struct fr_data *sfr; - - if (!(ch->init_status & HW_OPEN)) { - return -ENODEV; - } - - if (fr->keepa_freq) { - del_timer(&fr->keepa_timer); - } - - for (; dir ; dir = dir->next) { - if(!S_ISDIR(dir->mode)) { - continue; - } - if ((sdev = dir->data) && (sch = sdev->priv) && - (sdev->type == ARPHRD_DLCI) && - (sfr = sch->LINE_privdata) && - (sfr->master == dev) && - (sch->init_status & LINE_OPEN)) { - dev_close(sdev); - } - } - } - - ch->init_status &= ~LINE_OPEN; - ch->line_status &= ~(PROTO_UP | PROTO_LOOP); - dev->flags &= ~IFF_RUNNING; - - for (; comxdir ; comxdir = comxdir->next) { - if (strcmp(comxdir->name, FILENAME_DLCI) == 0 || - strcmp(comxdir->name, FILENAME_MASTER) == 0 || - strcmp(comxdir->name, FILENAME_KEEPALIVE) == 0) { - comxdir->mode = S_IFREG | 0444; - } - } - - return 0; -} - -static int fr_xmit(struct sk_buff *skb, struct net_device *dev) -{ - struct comx_channel *ch = dev->priv; - struct comx_channel *sch, *mch; - struct fr_data *fr = ch->LINE_privdata; - struct fr_data *sfr; - struct net_device *sdev; - struct proc_dir_entry *dir = ch->procdir->parent->subdir; - - if (!fr->master) { - printk(KERN_ERR "BUG: fr_xmit without a master!!! dev: %s\n", dev->name); - return 0; - } - - mch = fr->master->priv; - - /* Ennek majd a slave utemezeskor lesz igazan jelentosege */ - if (ch->debug_flags & DEBUG_COMX_DLCI) { - comx_debug_skb(dev, skb, "Sending frame"); - } - - if (dev != fr->master) { - struct sk_buff *newskb=skb_clone(skb, GFP_ATOMIC); - if (!newskb) - return -ENOMEM; - newskb->dev=fr->master; - dev_queue_xmit(newskb); - ch->stats.tx_bytes += skb->len; - ch->stats.tx_packets++; - dev_kfree_skb(skb); - } else { - netif_stop_queue(dev); - for (; dir ; dir = dir->next) { - if(!S_ISDIR(dir->mode)) { - continue; - } - if ((sdev = dir->data) && (sch = sdev->priv) && - (sdev->type == ARPHRD_DLCI) && (sfr = sch->LINE_privdata) && - (sfr->master == dev) && (netif_queue_stopped(sdev))) { - netif_stop_queue(sdev); - } - } - - switch(mch->HW_send_packet(dev, skb)) { - case FRAME_QUEUED: - netif_wake_queue(dev); - break; - case FRAME_ACCEPTED: - case FRAME_DROPPED: - break; - case FRAME_ERROR: - printk(KERN_ERR "%s: Transmit frame error (len %d)\n", - dev->name, skb->len); - break; - } - } - return 0; -} - -static int fr_header(struct sk_buff *skb, struct net_device *dev, - unsigned short type, void *daddr, void *saddr, unsigned len) -{ - struct comx_channel *ch = dev->priv; - struct fr_data *fr = ch->LINE_privdata; - - skb_push(skb, dev->hard_header_len); - /* Put in DLCI */ - skb->data[0] = (fr->dlci & (1024 - 15)) >> 2; - skb->data[1] = (fr->dlci & 15) << 4 | 1; // EA bit 1 - skb->data[2] = FRAD_UI; - skb->data[3] = NLPID_IP; - - return dev->hard_header_len; -} - -static int fr_statistics(struct net_device *dev, char *page) -{ - struct comx_channel *ch = dev->priv; - struct fr_data *fr = ch->LINE_privdata; - int len = 0; - - if (fr->master == dev) { - struct proc_dir_entry *dir = ch->procdir->parent->subdir; - struct net_device *sdev; - struct comx_channel *sch; - struct fr_data *sfr; - int slaves = 0; - - len += sprintf(page + len, - "This is a Frame Relay master device\nSlaves: "); - for (; dir ; dir = dir->next) { - if(!S_ISDIR(dir->mode)) { - continue; - } - if ((sdev = dir->data) && (sch = sdev->priv) && - (sdev->type == ARPHRD_DLCI) && - (sfr = sch->LINE_privdata) && - (sfr->master == dev) && (sdev != dev)) { - slaves++; - len += sprintf(page + len, "%s ", sdev->name); - } - } - len += sprintf(page + len, "%s\n", slaves ? "" : "(none)"); - if (fr->keepa_freq) { - len += sprintf(page + len, "Line keepalive (value %d) " - "status %s [%d]\n", fr->keepa_freq, - ch->line_status & PROTO_LOOP ? "LOOP" : - ch->line_status & PROTO_UP ? "UP" : "DOWN", - fr->keepalivecnt); - } else { - len += sprintf(page + len, "Line keepalive protocol " - "is not set\n"); - } - } else { // if slave - len += sprintf(page + len, - "This is a Frame Relay slave device, master: %s\n", - fr->master ? fr->master->name : "(not set)"); - } - return len; -} - -static int fr_read_proc(char *page, char **start, off_t off, int count, - int *eof, void *data) -{ - struct proc_dir_entry *file = (struct proc_dir_entry *)data; - struct net_device *dev = file->parent->data; - struct comx_channel *ch = dev->priv; - struct fr_data *fr = NULL; - int len = 0; - - if (ch) { - fr = ch->LINE_privdata; - } - - if (strcmp(file->name, FILENAME_DLCI) == 0) { - len = sprintf(page, "%04d\n", fr->dlci); - } else if (strcmp(file->name, FILENAME_MASTER) == 0) { - len = sprintf(page, "%-9s\n", fr->master ? fr->master->name : - "(none)"); - } else if (strcmp(file->name, FILENAME_KEEPALIVE) == 0) { - len = fr->keepa_freq ? sprintf(page, "% 3d\n", fr->keepa_freq) - : sprintf(page, "off\n"); - } else { - printk(KERN_ERR "comxfr: internal error, filename %s\n", file->name); - return -EBADF; - } - - if (off >= len) { - *eof = 1; - return 0; - } - - *start = page + off; - if (count >= len - off) *eof = 1; - return min_t(int, count, len - off); -} - -static int fr_write_proc(struct file *file, const char *buffer, - u_long count, void *data) -{ - struct proc_dir_entry *entry = (struct proc_dir_entry *)data; - struct net_device *dev = entry->parent->data; - struct comx_channel *ch = dev->priv; - struct fr_data *fr = NULL; - char *page; - - if (ch) { - fr = ch->LINE_privdata; - } - - if (!(page = (char *)__get_free_page(GFP_KERNEL))) { - return -ENOMEM; - } - - if (copy_from_user(page, buffer, count)) { - free_page((unsigned long)page); - return -EFAULT; - } - if (*(page + count - 1) == '\n') { - *(page + count - 1) = 0; - } - - if (strcmp(entry->name, FILENAME_DLCI) == 0) { - u16 dlci_new = simple_strtoul(page, NULL, 10); - - if (dlci_new > 1023) { - printk(KERN_ERR "Invalid DLCI value\n"); - } - else fr->dlci = dlci_new; - } else if (strcmp(entry->name, FILENAME_MASTER) == 0) { - struct net_device *new_master = dev_get_by_name(page); - - if (new_master && new_master->type == ARPHRD_FRAD) { - struct comx_channel *sch = new_master->priv; - struct fr_data *sfr = sch->LINE_privdata; - - if (sfr && sfr->master == new_master) { - if(fr->master) - dev_put(fr->master); - fr->master = new_master; - /* Megorokli a master statuszat */ - ch->line_status = sch->line_status; - } - } - } else if (strcmp(entry->name, FILENAME_KEEPALIVE) == 0) { - int keepa_new = -1; - - if (strcmp(page, KEEPALIVE_OFF) == 0) { - keepa_new = 0; - } else { - keepa_new = simple_strtoul(page, NULL, 10); - } - - if (keepa_new < 0 || keepa_new > 100) { - printk(KERN_ERR "invalid keepalive\n"); - } else { - if (fr->keepa_freq && keepa_new != fr->keepa_freq) { - fr_set_keepalive(dev, 0); - } - if (keepa_new) { - fr_set_keepalive(dev, keepa_new); - } - } - } else { - printk(KERN_ERR "comxfr_write_proc: internal error, filename %s\n", - entry->name); - count = -EBADF; - } - - free_page((unsigned long)page); - return count; -} - -static int fr_exit(struct net_device *dev) -{ - struct comx_channel *ch = dev->priv; - struct fr_data *fr = ch->LINE_privdata; - struct net_device *sdev = dev; - struct comx_channel *sch; - struct fr_data *sfr; - struct proc_dir_entry *dir = ch->procdir->parent->subdir; - - /* Ha lezarunk egy master-t, le kell kattintani a slave-eket is */ - if (fr->master && fr->master == dev) { - for (; dir ; dir = dir->next) { - if(!S_ISDIR(dir->mode)) { - continue; - } - if ((sdev = dir->data) && (sch = sdev->priv) && - (sdev->type == ARPHRD_DLCI) && - (sfr = sch->LINE_privdata) && (sfr->master == dev)) { - dev_close(sdev); - sfr->master = NULL; - } - } - } - dev->flags = 0; - dev->type = 0; - dev->mtu = 0; - dev->hard_header_len = 0; - - ch->LINE_rx = NULL; - ch->LINE_tx = NULL; - ch->LINE_status = NULL; - ch->LINE_open = NULL; - ch->LINE_close = NULL; - ch->LINE_xmit = NULL; - ch->LINE_header = NULL; - ch->LINE_rebuild_header = NULL; - ch->LINE_statistics = NULL; - - ch->LINE_status = 0; - - if (fr->master != dev) { // if not master, remove dlci - if(fr->master) - dev_put(fr->master); - remove_proc_entry(FILENAME_DLCI, ch->procdir); - remove_proc_entry(FILENAME_MASTER, ch->procdir); - } else { - if (fr->keepa_freq) { - fr_set_keepalive(dev, 0); - } - remove_proc_entry(FILENAME_KEEPALIVE, ch->procdir); - remove_proc_entry(FILENAME_DLCI, ch->procdir); - } - - kfree(fr); - ch->LINE_privdata = NULL; - - MOD_DEC_USE_COUNT; - return 0; -} - -static int fr_master_init(struct net_device *dev) -{ - struct comx_channel *ch = dev->priv; - struct fr_data *fr; - struct proc_dir_entry *new_file; - - if ((fr = ch->LINE_privdata = kmalloc(sizeof(struct fr_data), - GFP_KERNEL)) == NULL) { - return -ENOMEM; - } - memset(fr, 0, sizeof(struct fr_data)); - fr->master = dev; // this means master - fr->dlci = 0; // let's say default - - dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST; - dev->type = ARPHRD_FRAD; - dev->mtu = 1500; - dev->hard_header_len = 4; - dev->addr_len = 0; - - ch->LINE_rx = fr_rx; - ch->LINE_tx = fr_tx; - ch->LINE_status = fr_status; - ch->LINE_open = fr_open; - ch->LINE_close = fr_close; - ch->LINE_xmit = fr_xmit; - ch->LINE_header = fr_header; - ch->LINE_rebuild_header = NULL; - ch->LINE_statistics = fr_statistics; - - if ((new_file = create_proc_entry(FILENAME_DLCI, S_IFREG | 0644, - ch->procdir)) == NULL) { - goto cleanup_LINE_privdata; - } - new_file->data = (void *)new_file; - new_file->read_proc = &fr_read_proc; - new_file->write_proc = &fr_write_proc; - new_file->size = 5; - new_file->nlink = 1; - - if ((new_file = create_proc_entry(FILENAME_KEEPALIVE, S_IFREG | 0644, - ch->procdir)) == NULL) { - goto cleanup_filename_dlci; - } - new_file->data = (void *)new_file; - new_file->read_proc = &fr_read_proc; - new_file->write_proc = &fr_write_proc; - new_file->size = 4; - new_file->nlink = 1; - - fr_set_keepalive(dev, 0); - - MOD_INC_USE_COUNT; - return 0; -cleanup_filename_dlci: - remove_proc_entry(FILENAME_DLCI, ch->procdir); -cleanup_LINE_privdata: - kfree(fr); - return -EIO; -} - -static int fr_slave_init(struct net_device *dev) -{ - struct comx_channel *ch = dev->priv; - struct fr_data *fr; - struct proc_dir_entry *new_file; - - if ((fr = ch->LINE_privdata = kmalloc(sizeof(struct fr_data), - GFP_KERNEL)) == NULL) { - return -ENOMEM; - } - memset(fr, 0, sizeof(struct fr_data)); - - dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST; - dev->type = ARPHRD_DLCI; - dev->mtu = 1500; - dev->hard_header_len = 4; - dev->addr_len = 0; - - ch->LINE_rx = fr_rx; - ch->LINE_tx = fr_tx; - ch->LINE_status = fr_status; - ch->LINE_open = fr_open; - ch->LINE_close = fr_close; - ch->LINE_xmit = fr_xmit; - ch->LINE_header = fr_header; - ch->LINE_rebuild_header = NULL; - ch->LINE_statistics = fr_statistics; - - if ((new_file = create_proc_entry(FILENAME_DLCI, S_IFREG | 0644, - ch->procdir)) == NULL) { - goto cleanup_LINE_privdata; - } - - new_file->data = (void *)new_file; - new_file->read_proc = &fr_read_proc; - new_file->write_proc = &fr_write_proc; - new_file->size = 5; - new_file->nlink = 1; - - if ((new_file = create_proc_entry(FILENAME_MASTER, S_IFREG | 0644, - ch->procdir)) == NULL) { - goto cleanup_filename_dlci; - } - new_file->data = (void *)new_file; - new_file->read_proc = &fr_read_proc; - new_file->write_proc = &fr_write_proc; - new_file->size = 10; - new_file->nlink = 1; - MOD_INC_USE_COUNT; - return 0; -cleanup_filename_dlci: - remove_proc_entry(FILENAME_DLCI, ch->procdir); -cleanup_LINE_privdata: - kfree(fr); - return -EIO; -} - -static int dlci_open(struct net_device *dev) -{ - struct comx_channel *ch = dev->priv; - - ch->init_status |= HW_OPEN; - - MOD_INC_USE_COUNT; - return 0; -} - -static int dlci_close(struct net_device *dev) -{ - struct comx_channel *ch = dev->priv; - - ch->init_status &= ~HW_OPEN; - - MOD_DEC_USE_COUNT; - return 0; -} - -static int dlci_txe(struct net_device *dev) -{ - struct comx_channel *ch = dev->priv; - struct fr_data *fr = ch->LINE_privdata; - - if (!fr->master) { - return 0; - } - - ch = fr->master->priv; - fr = ch->LINE_privdata; - return ch->HW_txe(fr->master); -} - -static int dlci_statistics(struct net_device *dev, char *page) -{ - return 0; -} - -static int dlci_init(struct net_device *dev) -{ - struct comx_channel *ch = dev->priv; - - ch->HW_open = dlci_open; - ch->HW_close = dlci_close; - ch->HW_txe = dlci_txe; - ch->HW_statistics = dlci_statistics; - - /* Nincs egyeb hw info, mert ugyis a fr->master-bol fog minden kiderulni */ - - MOD_INC_USE_COUNT; - return 0; -} - -static int dlci_exit(struct net_device *dev) -{ - struct comx_channel *ch = dev->priv; - - ch->HW_open = NULL; - ch->HW_close = NULL; - ch->HW_txe = NULL; - ch->HW_statistics = NULL; - - MOD_DEC_USE_COUNT; - return 0; -} - -static int dlci_dump(struct net_device *dev) -{ - printk(KERN_INFO "dlci_dump %s, HOGY MI ???\n", dev->name); - return -1; -} - -static struct comx_protocol fr_master_protocol = { - .name = "frad", - .version = VERSION, - .encap_type = ARPHRD_FRAD, - .line_init = fr_master_init, - .line_exit = fr_exit, -}; - -static struct comx_protocol fr_slave_protocol = { - .name = "ietf-ip", - .version = VERSION, - .encap_type = ARPHRD_DLCI, - .line_init = fr_slave_init, - .line_exit = fr_exit, -}; - -static struct comx_hardware fr_dlci = { - .name = "dlci", - .version = VERSION, - .hw_init = dlci_init, - .hw_exit = dlci_exit, - .hw_dump = dlci_dump, -}; - -static int __init comx_proto_fr_init(void) -{ - int ret; - - if ((ret = comx_register_hardware(&fr_dlci))) { - return ret; - } - if ((ret = comx_register_protocol(&fr_master_protocol))) { - return ret; - } - return comx_register_protocol(&fr_slave_protocol); -} - -static void __exit comx_proto_fr_exit(void) -{ - comx_unregister_hardware(fr_dlci.name); - comx_unregister_protocol(fr_master_protocol.name); - comx_unregister_protocol(fr_slave_protocol.name); -} - -module_init(comx_proto_fr_init); -module_exit(comx_proto_fr_exit); diff --git a/drivers/net/wan/comx-proto-lapb.c b/drivers/net/wan/comx-proto-lapb.c deleted file mode 100644 index b203ff689..000000000 --- a/drivers/net/wan/comx-proto-lapb.c +++ /dev/null @@ -1,551 +0,0 @@ -/* - * LAPB protocol module for the COMX driver - * for Linux kernel 2.2.X - * - * Original author: Tivadar Szemethy - * Maintainer: Gergely Madarasz - * - * Copyright (C) 1997-1999 (C) ITConsult-Pro Co. - * - * 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. - * - * Version 0.80 (99/06/14): - * - cleaned up the source code a bit - * - ported back to kernel, now works as non-module - * - * Changed (00/10/29, Henner Eisen): - * - comx_rx() / comxlapb_data_indication() return status. - * - */ - -#define VERSION "0.80" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "comx.h" -#include "comxhw.h" - -static struct proc_dir_entry *create_comxlapb_proc_entry(char *name, int mode, - int size, struct proc_dir_entry *dir); - -static void comxlapb_rx(struct net_device *dev, struct sk_buff *skb) -{ - if (!dev || !dev->priv) { - dev_kfree_skb(skb); - } else { - lapb_data_received(dev, skb); - } -} - -static int comxlapb_tx(struct net_device *dev) -{ - netif_wake_queue(dev); - return 0; -} - -static int comxlapb_header(struct sk_buff *skb, struct net_device *dev, - unsigned short type, void *daddr, void *saddr, unsigned len) -{ - return dev->hard_header_len; -} - -static void comxlapb_status(struct net_device *dev, unsigned short status) -{ - struct comx_channel *ch; - - if (!dev || !(ch = dev->priv)) { - return; - } - if (status & LINE_UP) { - netif_wake_queue(dev); - } - comx_status(dev, status); -} - -static int comxlapb_open(struct net_device *dev) -{ - struct comx_channel *ch = dev->priv; - int err = 0; - - if (!(ch->init_status & HW_OPEN)) { - return -ENODEV; - } - - err = lapb_connect_request(dev); - - if (ch->debug_flags & DEBUG_COMX_LAPB) { - comx_debug(dev, "%s: lapb opened, error code: %d\n", - dev->name, err); - } - - if (!err) { - ch->init_status |= LINE_OPEN; - MOD_INC_USE_COUNT; - } - return err; -} - -static int comxlapb_close(struct net_device *dev) -{ - struct comx_channel *ch = dev->priv; - - if (!(ch->init_status & HW_OPEN)) { - return -ENODEV; - } - - if (ch->debug_flags & DEBUG_COMX_LAPB) { - comx_debug(dev, "%s: lapb closed\n", dev->name); - } - - lapb_disconnect_request(dev); - - ch->init_status &= ~LINE_OPEN; - ch->line_status &= ~PROTO_UP; - MOD_DEC_USE_COUNT; - return 0; -} - -static int comxlapb_xmit(struct sk_buff *skb, struct net_device *dev) -{ - struct comx_channel *ch = dev->priv; - struct sk_buff *skb2; - - if (!dev || !(ch = dev->priv) || !(dev->flags & (IFF_UP | IFF_RUNNING))) { - return -ENODEV; - } - - if (dev->type == ARPHRD_X25) { // first byte tells what to do - switch(skb->data[0]) { - case 0x00: - break; // transmit - case 0x01: - lapb_connect_request(dev); - kfree_skb(skb); - return 0; - case 0x02: - lapb_disconnect_request(dev); - default: - kfree_skb(skb); - return 0; - } - skb_pull(skb,1); - } - - netif_stop_queue(dev); - - if ((skb2 = skb_clone(skb, GFP_ATOMIC)) != NULL) { - lapb_data_request(dev, skb2); - } - - return FRAME_ACCEPTED; -} - -static int comxlapb_statistics(struct net_device *dev, char *page) -{ - struct lapb_parms_struct parms; - int len = 0; - - len += sprintf(page + len, "Line status: "); - if (lapb_getparms(dev, &parms) != LAPB_OK) { - len += sprintf(page + len, "not initialized\n"); - return len; - } - len += sprintf(page + len, "%s (%s), T1: %d/%d, T2: %d/%d, N2: %d/%d, " - "window: %d\n", parms.mode & LAPB_DCE ? "DCE" : "DTE", - parms.mode & LAPB_EXTENDED ? "EXTENDED" : "STANDARD", - parms.t1timer, parms.t1, parms.t2timer, parms.t2, - parms.n2count, parms.n2, parms.window); - - return len; -} - -static int comxlapb_read_proc(char *page, char **start, off_t off, int count, - int *eof, void *data) -{ - struct proc_dir_entry *file = (struct proc_dir_entry *)data; - struct net_device *dev = file->parent->data; - struct lapb_parms_struct parms; - int len = 0; - - if (lapb_getparms(dev, &parms)) { - return -ENODEV; - } - - if (strcmp(file->name, FILENAME_T1) == 0) { - len += sprintf(page + len, "%02u / %02u\n", - parms.t1timer, parms.t1); - } else if (strcmp(file->name, FILENAME_T2) == 0) { - len += sprintf(page + len, "%02u / %02u\n", - parms.t2timer, parms.t2); - } else if (strcmp(file->name, FILENAME_N2) == 0) { - len += sprintf(page + len, "%02u / %02u\n", - parms.n2count, parms.n2); - } else if (strcmp(file->name, FILENAME_WINDOW) == 0) { - len += sprintf(page + len, "%u\n", parms.window); - } else if (strcmp(file->name, FILENAME_MODE) == 0) { - len += sprintf(page + len, "%s, %s\n", - parms.mode & LAPB_DCE ? "DCE" : "DTE", - parms.mode & LAPB_EXTENDED ? "EXTENDED" : "STANDARD"); - } else { - printk(KERN_ERR "comxlapb: internal error, filename %s\n", file->name); - return -EBADF; - } - - if (off >= len) { - *eof = 1; - return 0; - } - - *start = page + off; - if (count >= len - off) { - *eof = 1; - } - return min_t(int, count, len - off); -} - -static int comxlapb_write_proc(struct file *file, const char *buffer, - u_long count, void *data) -{ - struct proc_dir_entry *entry = (struct proc_dir_entry *)data; - struct net_device *dev = entry->parent->data; - struct lapb_parms_struct parms; - unsigned long parm; - char *page; - - if (lapb_getparms(dev, &parms)) { - return -ENODEV; - } - - if (!(page = (char *)__get_free_page(GFP_KERNEL))) { - return -ENOMEM; - } - - if (copy_from_user(page, buffer, count)) { - free_page((unsigned long)page); - return -EFAULT; - } - if (*(page + count - 1) == '\n') { - *(page + count - 1) = 0; - } - - if (strcmp(entry->name, FILENAME_T1) == 0) { - parm=simple_strtoul(page,NULL,10); - if (parm > 0 && parm < 100) { - parms.t1=parm; - lapb_setparms(dev, &parms); - } - } else if (strcmp(entry->name, FILENAME_T2) == 0) { - parm=simple_strtoul(page, NULL, 10); - if (parm > 0 && parm < 100) { - parms.t2=parm; - lapb_setparms(dev, &parms); - } - } else if (strcmp(entry->name, FILENAME_N2) == 0) { - parm=simple_strtoul(page, NULL, 10); - if (parm > 0 && parm < 100) { - parms.n2=parm; - lapb_setparms(dev, &parms); - } - } else if (strcmp(entry->name, FILENAME_WINDOW) == 0) { - parms.window = simple_strtoul(page, NULL, 10); - lapb_setparms(dev, &parms); - } else if (strcmp(entry->name, FILENAME_MODE) == 0) { - if (comx_strcasecmp(page, "dte") == 0) { - parms.mode &= ~(LAPB_DCE | LAPB_DTE); - parms.mode |= LAPB_DTE; - } else if (comx_strcasecmp(page, "dce") == 0) { - parms.mode &= ~(LAPB_DTE | LAPB_DCE); - parms.mode |= LAPB_DCE; - } else if (comx_strcasecmp(page, "std") == 0 || - comx_strcasecmp(page, "standard") == 0) { - parms.mode &= ~LAPB_EXTENDED; - parms.mode |= LAPB_STANDARD; - } else if (comx_strcasecmp(page, "ext") == 0 || - comx_strcasecmp(page, "extended") == 0) { - parms.mode &= ~LAPB_STANDARD; - parms.mode |= LAPB_EXTENDED; - } - lapb_setparms(dev, &parms); - } else { - printk(KERN_ERR "comxlapb_write_proc: internal error, filename %s\n", - entry->name); - return -EBADF; - } - - free_page((unsigned long)page); - return count; -} - -static void comxlapb_connected(struct net_device *dev, int reason) -{ - struct comx_channel *ch = dev->priv; - struct proc_dir_entry *comxdir = ch->procdir->subdir; - - if (ch->debug_flags & DEBUG_COMX_LAPB) { - comx_debug(ch->dev, "%s: lapb connected, reason: %d\n", - ch->dev->name, reason); - } - - if (ch->dev->type == ARPHRD_X25) { - unsigned char *p; - struct sk_buff *skb; - - if ((skb = dev_alloc_skb(1)) == NULL) { - printk(KERN_ERR "comxlapb: out of memory!\n"); - return; - } - p = skb_put(skb,1); - *p = 0x01; // link established - skb->dev = ch->dev; - skb->protocol = htons(ETH_P_X25); - skb->mac.raw = skb->data; - skb->pkt_type = PACKET_HOST; - - netif_rx(skb); - ch->dev->last_rx = jiffies; - } - - for (; comxdir; comxdir = comxdir->next) { - if (strcmp(comxdir->name, FILENAME_MODE) == 0) { - comxdir->mode = S_IFREG | 0444; - } - } - - - ch->line_status |= PROTO_UP; - comx_status(ch->dev, ch->line_status); -} - -static void comxlapb_disconnected(struct net_device *dev, int reason) -{ - struct comx_channel *ch = dev->priv; - struct proc_dir_entry *comxdir = ch->procdir->subdir; - - if (ch->debug_flags & DEBUG_COMX_LAPB) { - comx_debug(ch->dev, "%s: lapb disconnected, reason: %d\n", - ch->dev->name, reason); - } - - if (ch->dev->type == ARPHRD_X25) { - unsigned char *p; - struct sk_buff *skb; - - if ((skb = dev_alloc_skb(1)) == NULL) { - printk(KERN_ERR "comxlapb: out of memory!\n"); - return; - } - p = skb_put(skb,1); - *p = 0x02; // link disconnected - skb->dev = ch->dev; - skb->protocol = htons(ETH_P_X25); - skb->mac.raw = skb->data; - skb->pkt_type = PACKET_HOST; - - netif_rx(skb); - ch->dev->last_rx = jiffies; - } - - for (; comxdir; comxdir = comxdir->next) { - if (strcmp(comxdir->name, FILENAME_MODE) == 0) { - comxdir->mode = S_IFREG | 0644; - } - } - - ch->line_status &= ~PROTO_UP; - comx_status(ch->dev, ch->line_status); -} - -static int comxlapb_data_indication(struct net_device *dev, struct sk_buff *skb) -{ - struct comx_channel *ch = dev->priv; - - if (ch->dev->type == ARPHRD_X25) { - skb_push(skb, 1); - - if (skb_cow(skb, 1)) - return NET_RX_DROP; - - skb->data[0] = 0; // indicate data for X25 - skb->protocol = htons(ETH_P_X25); - } else { - skb->protocol = htons(ETH_P_IP); - } - - skb->dev = ch->dev; - skb->mac.raw = skb->data; - return comx_rx(ch->dev, skb); -} - -static void comxlapb_data_transmit(struct net_device *dev, struct sk_buff *skb) -{ - struct comx_channel *ch = dev->priv; - - if (ch->HW_send_packet) { - ch->HW_send_packet(ch->dev, skb); - } -} - -static int comxlapb_exit(struct net_device *dev) -{ - struct comx_channel *ch = dev->priv; - - dev->flags = 0; - dev->type = 0; - dev->mtu = 0; - dev->hard_header_len = 0; - - ch->LINE_rx = NULL; - ch->LINE_tx = NULL; - ch->LINE_status = NULL; - ch->LINE_open = NULL; - ch->LINE_close = NULL; - ch->LINE_xmit = NULL; - ch->LINE_header = NULL; - ch->LINE_statistics = NULL; - - if (ch->debug_flags & DEBUG_COMX_LAPB) { - comx_debug(dev, "%s: unregistering lapb\n", dev->name); - } - lapb_unregister(dev); - - remove_proc_entry(FILENAME_T1, ch->procdir); - remove_proc_entry(FILENAME_T2, ch->procdir); - remove_proc_entry(FILENAME_N2, ch->procdir); - remove_proc_entry(FILENAME_MODE, ch->procdir); - remove_proc_entry(FILENAME_WINDOW, ch->procdir); - - MOD_DEC_USE_COUNT; - return 0; -} - -static int comxlapb_init(struct net_device *dev) -{ - struct comx_channel *ch = dev->priv; - struct lapb_register_struct lapbreg; - - dev->mtu = 1500; - dev->hard_header_len = 4; - dev->addr_len = 0; - - ch->LINE_rx = comxlapb_rx; - ch->LINE_tx = comxlapb_tx; - ch->LINE_status = comxlapb_status; - ch->LINE_open = comxlapb_open; - ch->LINE_close = comxlapb_close; - ch->LINE_xmit = comxlapb_xmit; - ch->LINE_header = comxlapb_header; - ch->LINE_statistics = comxlapb_statistics; - - lapbreg.connect_confirmation = comxlapb_connected; - lapbreg.connect_indication = comxlapb_connected; - lapbreg.disconnect_confirmation = comxlapb_disconnected; - lapbreg.disconnect_indication = comxlapb_disconnected; - lapbreg.data_indication = comxlapb_data_indication; - lapbreg.data_transmit = comxlapb_data_transmit; - if (lapb_register(dev, &lapbreg)) { - return -ENOMEM; - } - if (ch->debug_flags & DEBUG_COMX_LAPB) { - comx_debug(dev, "%s: lapb registered\n", dev->name); - } - - if (!create_comxlapb_proc_entry(FILENAME_T1, 0644, 8, ch->procdir)) { - return -ENOMEM; - } - if (!create_comxlapb_proc_entry(FILENAME_T2, 0644, 8, ch->procdir)) { - return -ENOMEM; - } - if (!create_comxlapb_proc_entry(FILENAME_N2, 0644, 8, ch->procdir)) { - return -ENOMEM; - } - if (!create_comxlapb_proc_entry(FILENAME_MODE, 0644, 14, ch->procdir)) { - return -ENOMEM; - } - if (!create_comxlapb_proc_entry(FILENAME_WINDOW, 0644, 0, ch->procdir)) { - return -ENOMEM; - } - - MOD_INC_USE_COUNT; - return 0; -} - -static int comxlapb_init_lapb(struct net_device *dev) -{ - dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST; - dev->type = ARPHRD_LAPB; - - return(comxlapb_init(dev)); -} - -static int comxlapb_init_x25(struct net_device *dev) -{ - dev->flags = IFF_NOARP; - dev->type = ARPHRD_X25; - - return(comxlapb_init(dev)); -} - -static struct proc_dir_entry *create_comxlapb_proc_entry(char *name, int mode, - int size, struct proc_dir_entry *dir) -{ - struct proc_dir_entry *new_file; - - if ((new_file = create_proc_entry(name, S_IFREG | mode, dir)) != NULL) { - new_file->data = (void *)new_file; - new_file->read_proc = &comxlapb_read_proc; - new_file->write_proc = &comxlapb_write_proc; - new_file->size = size; - new_file->nlink = 1; - } - return(new_file); -} - -static struct comx_protocol comxlapb_protocol = { - "lapb", - VERSION, - ARPHRD_LAPB, - comxlapb_init_lapb, - comxlapb_exit, - NULL -}; - -static struct comx_protocol comx25_protocol = { - "x25", - VERSION, - ARPHRD_X25, - comxlapb_init_x25, - comxlapb_exit, - NULL -}; - -static int __init comx_proto_lapb_init(void) -{ - int ret; - - if ((ret = comx_register_protocol(&comxlapb_protocol)) != 0) { - return ret; - } - return comx_register_protocol(&comx25_protocol); -} - -static void __exit comx_proto_lapb_exit(void) -{ - comx_unregister_protocol(comxlapb_protocol.name); - comx_unregister_protocol(comx25_protocol.name); -} - -module_init(comx_proto_lapb_init); -module_exit(comx_proto_lapb_exit); - -MODULE_LICENSE("GPL"); diff --git a/drivers/net/wan/comx-proto-ppp.c b/drivers/net/wan/comx-proto-ppp.c deleted file mode 100644 index 3f4501014..000000000 --- a/drivers/net/wan/comx-proto-ppp.c +++ /dev/null @@ -1,269 +0,0 @@ -/* - * Synchronous PPP / Cisco-HDLC driver for the COMX boards - * - * Author: Gergely Madarasz - * - * based on skeleton code by Tivadar Szemethy - * - * Copyright (C) 1999 ITConsult-Pro Co. - * - * 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. - * - * - * Version 0.10 (99/06/10): - * - written the first code :) - * - * Version 0.20 (99/06/16): - * - added hdlc protocol - * - protocol up is IFF_RUNNING - * - * Version 0.21 (99/07/15): - * - some small fixes with the line status - * - * Version 0.22 (99/08/05): - * - don't test IFF_RUNNING but the pp_link_state of the sppp - * - * Version 0.23 (99/12/02): - * - tbusy fixes - * - */ - -#define VERSION "0.23" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include "comx.h" - -MODULE_AUTHOR("Author: Gergely Madarasz "); -MODULE_DESCRIPTION("Cisco-HDLC / Synchronous PPP driver for the COMX sync serial boards"); -MODULE_LICENSE("GPL"); - -static struct comx_protocol syncppp_protocol; -static struct comx_protocol hdlc_protocol; - -struct syncppp_data { - struct timer_list status_timer; -}; - -static void syncppp_status_timerfun(unsigned long d) { - struct net_device *dev=(struct net_device *)d; - struct comx_channel *ch=dev->priv; - struct syncppp_data *spch=ch->LINE_privdata; - struct sppp *sp = (struct sppp *)sppp_of(dev); - - if(!(ch->line_status & PROTO_UP) && - (sp->pp_link_state==SPPP_LINK_UP)) { - comx_status(dev, ch->line_status | PROTO_UP); - } - if((ch->line_status & PROTO_UP) && - (sp->pp_link_state==SPPP_LINK_DOWN)) { - comx_status(dev, ch->line_status & ~PROTO_UP); - } - mod_timer(&spch->status_timer,jiffies + HZ*3); -} - -static int syncppp_tx(struct net_device *dev) -{ - struct comx_channel *ch=dev->priv; - - if(ch->line_status & LINE_UP) { - netif_wake_queue(dev); - } - return 0; -} - -static void syncppp_status(struct net_device *dev, unsigned short status) -{ - status &= ~(PROTO_UP | PROTO_LOOP); - if(status & LINE_UP) { - netif_wake_queue(dev); - sppp_open(dev); - } else { - /* Line went down */ - netif_stop_queue(dev); - sppp_close(dev); - } - comx_status(dev, status); -} - -static int syncppp_open(struct net_device *dev) -{ - struct comx_channel *ch = dev->priv; - struct syncppp_data *spch = ch->LINE_privdata; - - if (!(ch->init_status & HW_OPEN)) return -ENODEV; - - ch->init_status |= LINE_OPEN; - ch->line_status &= ~(PROTO_UP | PROTO_LOOP); - - if(ch->line_status & LINE_UP) { - sppp_open(dev); - } - - init_timer(&spch->status_timer); - spch->status_timer.function=syncppp_status_timerfun; - spch->status_timer.data=(unsigned long)dev; - spch->status_timer.expires=jiffies + HZ*3; - add_timer(&spch->status_timer); - - return 0; -} - -static int syncppp_close(struct net_device *dev) -{ - struct comx_channel *ch = dev->priv; - struct syncppp_data *spch = ch->LINE_privdata; - - if (!(ch->init_status & HW_OPEN)) return -ENODEV; - del_timer(&spch->status_timer); - - sppp_close(dev); - - ch->init_status &= ~LINE_OPEN; - ch->line_status &= ~(PROTO_UP | PROTO_LOOP); - - return 0; -} - -static int syncppp_xmit(struct sk_buff *skb, struct net_device *dev) -{ - struct comx_channel *ch = dev->priv; - - netif_stop_queue(dev); - switch(ch->HW_send_packet(dev, skb)) { - case FRAME_QUEUED: - netif_wake_queue(dev); - break; - case FRAME_ACCEPTED: - case FRAME_DROPPED: - break; - case FRAME_ERROR: - printk(KERN_ERR "%s: Transmit frame error (len %d)\n", - dev->name, skb->len); - break; - } - return 0; -} - - -static int syncppp_statistics(struct net_device *dev, char *page) -{ - int len = 0; - - len += sprintf(page + len, " "); - return len; -} - - -static int syncppp_exit(struct net_device *dev) -{ - struct comx_channel *ch = dev->priv; - - sppp_detach(dev); - - dev->flags = 0; - dev->type = 0; - dev->mtu = 0; - - ch->LINE_rx = NULL; - ch->LINE_tx = NULL; - ch->LINE_status = NULL; - ch->LINE_open = NULL; - ch->LINE_close = NULL; - ch->LINE_xmit = NULL; - ch->LINE_header = NULL; - ch->LINE_rebuild_header = NULL; - ch->LINE_statistics = NULL; - - kfree(ch->LINE_privdata); - ch->LINE_privdata = NULL; - - MOD_DEC_USE_COUNT; - return 0; -} - -static int syncppp_init(struct net_device *dev) -{ - struct comx_channel *ch = dev->priv; - struct ppp_device *pppdev = (struct ppp_device *)ch->if_ptr; - - ch->LINE_privdata = kmalloc(sizeof(struct syncppp_data), GFP_KERNEL); - if (!ch->LINE_privdata) - return -ENOMEM; - - pppdev->dev = dev; - sppp_attach(pppdev); - - if(ch->protocol == &hdlc_protocol) { - pppdev->sppp.pp_flags |= PP_CISCO; - dev->type = ARPHRD_HDLC; - } else { - pppdev->sppp.pp_flags &= ~PP_CISCO; - dev->type = ARPHRD_PPP; - } - - ch->LINE_rx = sppp_input; - ch->LINE_tx = syncppp_tx; - ch->LINE_status = syncppp_status; - ch->LINE_open = syncppp_open; - ch->LINE_close = syncppp_close; - ch->LINE_xmit = syncppp_xmit; - ch->LINE_header = NULL; - ch->LINE_statistics = syncppp_statistics; - - - MOD_INC_USE_COUNT; - return 0; -} - -static struct comx_protocol syncppp_protocol = { - "ppp", - VERSION, - ARPHRD_PPP, - syncppp_init, - syncppp_exit, - NULL -}; - -static struct comx_protocol hdlc_protocol = { - "hdlc", - VERSION, - ARPHRD_PPP, - syncppp_init, - syncppp_exit, - NULL -}; - -static int __init comx_proto_ppp_init(void) -{ - int ret; - - ret = comx_register_protocol(&hdlc_protocol); - if (!ret) { - ret = comx_register_protocol(&syncppp_protocol); - if (ret) - comx_unregister_protocol(hdlc_protocol.name); - } - return ret; -} - -static void __exit comx_proto_ppp_exit(void) -{ - comx_unregister_protocol(syncppp_protocol.name); - comx_unregister_protocol(hdlc_protocol.name); -} - -module_init(comx_proto_ppp_init); -module_exit(comx_proto_ppp_exit); diff --git a/drivers/net/wan/comx.c b/drivers/net/wan/comx.c deleted file mode 100644 index 6c0e3fcd2..000000000 --- a/drivers/net/wan/comx.c +++ /dev/null @@ -1,1128 +0,0 @@ -/* - * Device driver framework for the COMX line of synchronous serial boards - * - * for Linux kernel 2.2.X / 2.4.X - * - * Original authors: Arpad Bakay , - * Peter Bajan , - * Previous maintainer: Tivadar Szemethy - * Current maintainer: Gergely Madarasz - * - * Copyright (C) 1995-1999 ITConsult-Pro Co. - * - * Contributors: - * Arnaldo Carvalho de Melo (0.85) - * - * 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. - * - * Version 0.80 (99/06/11): - * - clean up source code (playing a bit of indent) - * - port back to kernel, add support for non-module versions - * - add support for board resets when channel protocol is down - * - reset the device structure after protocol exit - * the syncppp driver needs it - * - add support for /proc/comx/protocols and - * /proc/comx/boardtypes - * - * Version 0.81 (99/06/21): - * - comment out the board reset support code, the locomx - * driver seems not buggy now - * - printk() levels fixed - * - * Version 0.82 (99/07/08): - * - Handle stats correctly if the lowlevel driver is - * is not a comx one (locomx - z85230) - * - * Version 0.83 (99/07/15): - * - reset line_status when interface is down - * - * Version 0.84 (99/12/01): - * - comx_status should not check for IFF_UP (to report - * line status from dev->open()) - * - * Version 0.85 (00/08/15): - * - resource release on failure in comx_mkdir - * - fix return value on failure at comx_write_proc - * - * Changed (00/10/29, Henner Eisen): - * - comx_rx() / comxlapb_data_indication() return status. - */ - -#define VERSION "0.85" - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef CONFIG_KMOD -#include -#endif - -#ifndef CONFIG_PROC_FS -#error For now, COMX really needs the /proc filesystem -#endif - -#include -#include "comx.h" - -MODULE_AUTHOR("Gergely Madarasz "); -MODULE_DESCRIPTION("Common code for the COMX synchronous serial adapters"); -MODULE_LICENSE("GPL"); - -static struct comx_hardware *comx_channels = NULL; -static struct comx_protocol *comx_lines = NULL; - -static int comx_mkdir(struct inode *, struct dentry *, int); -static int comx_rmdir(struct inode *, struct dentry *); -static struct dentry *comx_lookup(struct inode *, struct dentry *, struct nameidata *); - -static struct inode_operations comx_root_inode_ops = { - .lookup = comx_lookup, - .mkdir = comx_mkdir, - .rmdir = comx_rmdir, -}; - -static int comx_delete_dentry(struct dentry *dentry); -static struct proc_dir_entry *create_comx_proc_entry(char *name, int mode, - int size, struct proc_dir_entry *dir); - -static struct dentry_operations comx_dentry_operations = { - .d_delete = comx_delete_dentry, -}; - - -static struct proc_dir_entry * comx_root_dir; - -struct comx_debugflags_struct comx_debugflags[] = { - { "comx_rx", DEBUG_COMX_RX }, - { "comx_tx", DEBUG_COMX_TX }, - { "hw_tx", DEBUG_HW_TX }, - { "hw_rx", DEBUG_HW_RX }, - { "hdlc_keepalive", DEBUG_HDLC_KEEPALIVE }, - { "comxppp", DEBUG_COMX_PPP }, - { "comxlapb", DEBUG_COMX_LAPB }, - { "dlci", DEBUG_COMX_DLCI }, - { NULL, 0 } -}; - - -int comx_debug(struct net_device *dev, char *fmt, ...) -{ - struct comx_channel *ch = dev->priv; - char *page,*str; - va_list args; - int len; - - if (!ch->debug_area) return 0; - - if (!(page = (char *)__get_free_page(GFP_ATOMIC))) return -ENOMEM; - - va_start(args, fmt); - len = vsprintf(str = page, fmt, args); - va_end(args); - - if (len >= PAGE_SIZE) { - printk(KERN_ERR "comx_debug: PANIC! len = %d !!!\n", len); - free_page((unsigned long)page); - return -EINVAL; - } - - while (len) { - int to_copy; - int free = (ch->debug_start - ch->debug_end + ch->debug_size) - % ch->debug_size; - - to_copy = min_t(int, free ? free : ch->debug_size, - min_t(int, ch->debug_size - ch->debug_end, len)); - memcpy(ch->debug_area + ch->debug_end, str, to_copy); - str += to_copy; - len -= to_copy; - ch->debug_end = (ch->debug_end + to_copy) % ch->debug_size; - if (ch->debug_start == ch->debug_end) // Full ? push start away - ch->debug_start = (ch->debug_start + len + 1) % - ch->debug_size; - ch->debug_file->size = (ch->debug_end - ch->debug_start + - ch->debug_size) % ch->debug_size; - } - - free_page((unsigned long)page); - return 0; -} - -int comx_debug_skb(struct net_device *dev, struct sk_buff *skb, char *msg) -{ - struct comx_channel *ch = dev->priv; - - if (!ch->debug_area) return 0; - if (!skb) comx_debug(dev, "%s: %s NULL skb\n\n", dev->name, msg); - if (!skb->len) comx_debug(dev, "%s: %s empty skb\n\n", dev->name, msg); - - return comx_debug_bytes(dev, skb->data, skb->len, msg); -} - -int comx_debug_bytes(struct net_device *dev, unsigned char *bytes, int len, - char *msg) -{ - int pos = 0; - struct comx_channel *ch = dev->priv; - - if (!ch->debug_area) return 0; - - comx_debug(dev, "%s: %s len %d\n", dev->name, msg, len); - - while (pos != len) { - char line[80]; - int i = 0; - - memset(line, 0, 80); - sprintf(line,"%04d ", pos); - do { - sprintf(line + 5 + (pos % 16) * 3, "%02x", bytes[pos]); - sprintf(line + 60 + (pos % 16), "%c", - isprint(bytes[pos]) ? bytes[pos] : '.'); - pos++; - } while (pos != len && pos % 16); - - while ( i++ != 78 ) if (line[i] == 0) line[i] = ' '; - line[77] = '\n'; - line[78] = 0; - - comx_debug(dev, "%s", line); - } - comx_debug(dev, "\n"); - return 0; -} - -static void comx_loadavg_timerfun(unsigned long d) -{ - struct net_device *dev = (struct net_device *)d; - struct comx_channel *ch = dev->priv; - - ch->avg_bytes[ch->loadavg_counter] = ch->current_stats->rx_bytes; - ch->avg_bytes[ch->loadavg_counter + ch->loadavg_size] = - ch->current_stats->tx_bytes; - - ch->loadavg_counter = (ch->loadavg_counter + 1) % ch->loadavg_size; - - mod_timer(&ch->loadavg_timer,jiffies + HZ * ch->loadavg[0]); -} - -#if 0 -static void comx_reset_timerfun(unsigned long d) -{ - struct net_device *dev = (struct net_device *)d; - struct comx_channel *ch = dev->priv; - - if(!(ch->line_status & (PROTO_LOOP | PROTO_UP))) { - if(test_and_set_bit(0,&ch->reset_pending) && ch->HW_reset) { - ch->HW_reset(dev); - } - } - - mod_timer(&ch->reset_timer, jiffies + HZ * ch->reset_timeout); -} -#endif - -static int comx_open(struct net_device *dev) -{ - struct comx_channel *ch = dev->priv; - struct proc_dir_entry *comxdir = ch->procdir->subdir; - int ret=0; - - if (!ch->protocol || !ch->hardware) return -ENODEV; - - if ((ret = ch->HW_open(dev))) return ret; - if ((ret = ch->LINE_open(dev))) { - ch->HW_close(dev); - return ret; - }; - - for (; comxdir ; comxdir = comxdir->next) { - if (strcmp(comxdir->name, FILENAME_HARDWARE) == 0 || - strcmp(comxdir->name, FILENAME_PROTOCOL) == 0) - comxdir->mode = S_IFREG | 0444; - } - -#if 0 - ch->reset_pending = 1; - ch->reset_timeout = 30; - ch->reset_timer.function = comx_reset_timerfun; - ch->reset_timer.data = (unsigned long)dev; - ch->reset_timer.expires = jiffies + HZ * ch->reset_timeout; - add_timer(&ch->reset_timer); -#endif - - return 0; -} - -static int comx_close(struct net_device *dev) -{ - struct comx_channel *ch = dev->priv; - struct proc_dir_entry *comxdir = ch->procdir->subdir; - int ret = -ENODEV; - - if (test_and_clear_bit(0, &ch->lineup_pending)) { - del_timer(&ch->lineup_timer); - } - -#if 0 - del_timer(&ch->reset_timer); -#endif - - if (ch->init_status & LINE_OPEN && ch->protocol && ch->LINE_close) { - ret = ch->LINE_close(dev); - } - - if (ret) return ret; - - if (ch->init_status & HW_OPEN && ch->hardware && ch->HW_close) { - ret = ch->HW_close(dev); - } - - ch->line_status=0; - - for (; comxdir ; comxdir = comxdir->next) { - if (strcmp(comxdir->name, FILENAME_HARDWARE) == 0 || - strcmp(comxdir->name, FILENAME_PROTOCOL) == 0) - comxdir->mode = S_IFREG | 0644; - } - - return ret; -} - -void comx_status(struct net_device *dev, int status) -{ - struct comx_channel *ch = dev->priv; - -#if 0 - if(status & (PROTO_UP | PROTO_LOOP)) { - clear_bit(0,&ch->reset_pending); - } -#endif - - printk(KERN_NOTICE "Interface %s: modem status %s, line protocol %s\n", - dev->name, status & LINE_UP ? "UP" : "DOWN", - status & PROTO_LOOP ? "LOOP" : status & PROTO_UP ? - "UP" : "DOWN"); - - ch->line_status = status; -} - -static int comx_xmit(struct sk_buff *skb, struct net_device *dev) -{ - struct comx_channel *ch = dev->priv; - int rc; - - if (skb->len > dev->mtu + dev->hard_header_len) { - printk(KERN_ERR "comx_xmit: %s: skb->len %d > dev->mtu %d\n", dev->name, - (int)skb->len, dev->mtu); - } - - if (ch->debug_flags & DEBUG_COMX_TX) { - comx_debug_skb(dev, skb, "comx_xmit skb"); - } - - rc=ch->LINE_xmit(skb, dev); -// if (!rc) dev_kfree_skb(skb); - - return rc; -} - -static int comx_header(struct sk_buff *skb, struct net_device *dev, - unsigned short type, void *daddr, void *saddr, unsigned len) -{ - struct comx_channel *ch = dev->priv; - - if (ch->LINE_header) { - return (ch->LINE_header(skb, dev, type, daddr, saddr, len)); - } else { - return 0; - } -} - -static int comx_rebuild_header(struct sk_buff *skb) -{ - struct net_device *dev = skb->dev; - struct comx_channel *ch = dev->priv; - - if (ch->LINE_rebuild_header) { - return(ch->LINE_rebuild_header(skb)); - } else { - return 0; - } -} - -int comx_rx(struct net_device *dev, struct sk_buff *skb) -{ - struct comx_channel *ch = dev->priv; - - if (ch->debug_flags & DEBUG_COMX_RX) { - comx_debug_skb(dev, skb, "comx_rx skb"); - } - if (skb) { - netif_rx(skb); - dev->last_rx = jiffies; - } - return 0; -} - -static struct net_device_stats *comx_stats(struct net_device *dev) -{ - struct comx_channel *ch = dev->priv; - - return ch->current_stats; -} - -void comx_lineup_func(unsigned long d) -{ - struct net_device *dev = (struct net_device *)d; - struct comx_channel *ch = dev->priv; - - del_timer(&ch->lineup_timer); - clear_bit(0, &ch->lineup_pending); - - if (ch->LINE_status) { - ch->LINE_status(dev, ch->line_status |= LINE_UP); - } -} - -#define LOADAVG(avg, off) (int) \ - ((ch->avg_bytes[(ch->loadavg_counter - 1 + ch->loadavg_size * 2) \ - % ch->loadavg_size + off] - ch->avg_bytes[(ch->loadavg_counter - 1 \ - - ch->loadavg[avg] / ch->loadavg[0] + ch->loadavg_size * 2) \ - % ch->loadavg_size + off]) / ch->loadavg[avg] * 8) - -static int comx_statistics(struct net_device *dev, char *page) -{ - struct comx_channel *ch = dev->priv; - int len = 0; - int tmp; - int i = 0; - char tmpstr[20]; - int tmpstrlen = 0; - - len += sprintf(page + len, "Interface administrative status is %s, " - "modem status is %s, protocol is %s\n", - dev->flags & IFF_UP ? "UP" : "DOWN", - ch->line_status & LINE_UP ? "UP" : "DOWN", - ch->line_status & PROTO_LOOP ? "LOOP" : - ch->line_status & PROTO_UP ? "UP" : "DOWN"); - len += sprintf(page + len, "Modem status changes: %lu, Transmitter status " - "is %s, tbusy: %d\n", ch->current_stats->tx_carrier_errors, ch->HW_txe ? - ch->HW_txe(dev) ? "IDLE" : "BUSY" : "NOT READY", netif_running(dev)); - len += sprintf(page + len, "Interface load (input): %d / %d / %d bits/s (", - LOADAVG(0,0), LOADAVG(1, 0), LOADAVG(2, 0)); - tmpstr[0] = 0; - for (i=0; i != 3; i++) { - char tf; - - tf = ch->loadavg[i] % 60 == 0 && - ch->loadavg[i] / 60 > 0 ? 'm' : 's'; - tmpstrlen += sprintf(tmpstr + tmpstrlen, "%d%c%s", - ch->loadavg[i] / (tf == 'm' ? 60 : 1), tf, - i == 2 ? ")\n" : "/"); - } - len += sprintf(page + len, - "%s (output): %d / %d / %d bits/s (%s", tmpstr, - LOADAVG(0,ch->loadavg_size), LOADAVG(1, ch->loadavg_size), - LOADAVG(2, ch->loadavg_size), tmpstr); - - len += sprintf(page + len, "Debug flags: "); - tmp = len; i = 0; - while (comx_debugflags[i].name) { - if (ch->debug_flags & comx_debugflags[i].value) - len += sprintf(page + len, "%s ", - comx_debugflags[i].name); - i++; - } - len += sprintf(page + len, "%s\n", tmp == len ? "none" : ""); - - len += sprintf(page + len, "RX errors: len: %lu, overrun: %lu, crc: %lu, " - "aborts: %lu\n buffer overrun: %lu, pbuffer overrun: %lu\n" - "TX errors: underrun: %lu\n", - ch->current_stats->rx_length_errors, ch->current_stats->rx_over_errors, - ch->current_stats->rx_crc_errors, ch->current_stats->rx_frame_errors, - ch->current_stats->rx_missed_errors, ch->current_stats->rx_fifo_errors, - ch->current_stats->tx_fifo_errors); - - if (ch->LINE_statistics && (ch->init_status & LINE_OPEN)) { - len += ch->LINE_statistics(dev, page + len); - } else { - len += sprintf(page+len, "Line status: driver not initialized\n"); - } - if (ch->HW_statistics && (ch->init_status & HW_OPEN)) { - len += ch->HW_statistics(dev, page + len); - } else { - len += sprintf(page+len, "Board status: driver not initialized\n"); - } - - return len; -} - -static int comx_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) -{ - struct comx_channel *ch = dev->priv; - - if (ch->LINE_ioctl) { - return(ch->LINE_ioctl(dev, ifr, cmd)); - } - return -EINVAL; -} - -static void comx_reset_dev(struct net_device *dev) -{ - dev->open = comx_open; - dev->stop = comx_close; - dev->hard_start_xmit = comx_xmit; - dev->hard_header = comx_header; - dev->rebuild_header = comx_rebuild_header; - dev->get_stats = comx_stats; - dev->do_ioctl = comx_ioctl; - dev->change_mtu = NULL; - dev->tx_queue_len = 20; - dev->flags = IFF_NOARP; -} - -static int comx_init_dev(struct net_device *dev) -{ - struct comx_channel *ch; - - if ((ch = kmalloc(sizeof(struct comx_channel), GFP_KERNEL)) == NULL) { - return -ENOMEM; - } - memset(ch, 0, sizeof(struct comx_channel)); - - ch->loadavg[0] = 5; - ch->loadavg[1] = 300; - ch->loadavg[2] = 900; - ch->loadavg_size = ch->loadavg[2] / ch->loadavg[0] + 1; - if ((ch->avg_bytes = kmalloc(ch->loadavg_size * - sizeof(unsigned long) * 2, GFP_KERNEL)) == NULL) { - kfree(ch); - return -ENOMEM; - } - - memset(ch->avg_bytes, 0, ch->loadavg_size * sizeof(unsigned long) * 2); - ch->loadavg_counter = 0; - ch->loadavg_timer.function = comx_loadavg_timerfun; - ch->loadavg_timer.data = (unsigned long)dev; - ch->loadavg_timer.expires = jiffies + HZ * ch->loadavg[0]; - add_timer(&ch->loadavg_timer); - - dev->priv = (void *)ch; - ch->dev = dev; - ch->line_status &= ~LINE_UP; - - ch->current_stats = &ch->stats; - - comx_reset_dev(dev); - return 0; -} - -static int comx_read_proc(char *page, char **start, off_t off, int count, - int *eof, void *data) -{ - struct proc_dir_entry *file = (struct proc_dir_entry *)data; - struct net_device *dev = file->parent->data; - struct comx_channel *ch = dev->priv; - int len = 0; - - if (strcmp(file->name, FILENAME_STATUS) == 0) { - len = comx_statistics(dev, page); - } else if (strcmp(file->name, FILENAME_HARDWARE) == 0) { - len = sprintf(page, "%s\n", ch->hardware ? - ch->hardware->name : HWNAME_NONE); - } else if (strcmp(file->name, FILENAME_PROTOCOL) == 0) { - len = sprintf(page, "%s\n", ch->protocol ? - ch->protocol->name : PROTONAME_NONE); - } else if (strcmp(file->name, FILENAME_LINEUPDELAY) == 0) { - len = sprintf(page, "%01d\n", ch->lineup_delay); - } - - if (off >= len) { - *eof = 1; - return 0; - } - - *start = page + off; - if (count >= len - off) { - *eof = 1; - } - return min_t(int, count, len - off); -} - - -static int comx_root_read_proc(char *page, char **start, off_t off, int count, - int *eof, void *data) -{ - struct proc_dir_entry *file = (struct proc_dir_entry *)data; - struct comx_hardware *hw; - struct comx_protocol *line; - - int len = 0; - - if (strcmp(file->name, FILENAME_HARDWARELIST) == 0) { - for(hw=comx_channels;hw;hw=hw->next) - len+=sprintf(page+len, "%s\n", hw->name); - } else if (strcmp(file->name, FILENAME_PROTOCOLLIST) == 0) { - for(line=comx_lines;line;line=line->next) - len+=sprintf(page+len, "%s\n", line->name); - } - - if (off >= len) { - *eof = 1; - return 0; - } - - *start = page + off; - if (count >= len - off) { - *eof = 1; - } - return min_t(int, count, len - off); -} - - - -static int comx_write_proc(struct file *file, const char *buffer, u_long count, - void *data) -{ - struct proc_dir_entry *entry = (struct proc_dir_entry *)data; - struct net_device *dev = (struct net_device *)entry->parent->data; - struct comx_channel *ch = dev->priv; - char *page; - struct comx_hardware *hw = comx_channels; - struct comx_protocol *line = comx_lines; - int ret=0; - - if (count > PAGE_SIZE) { - printk(KERN_ERR "count is %lu > %d!!!\n", count, (int)PAGE_SIZE); - return -ENOSPC; - } - - if (!(page = (char *)__get_free_page(GFP_KERNEL))) return -ENOMEM; - - if(copy_from_user(page, buffer, count)) - { - count = -EFAULT; - goto out; - } - - if (page[count-1] == '\n') - page[count-1] = '\0'; - else if (count < PAGE_SIZE) - page[count] = '\0'; - else if (page[count]) { - count = -EINVAL; - goto out; - } - - if (strcmp(entry->name, FILENAME_DEBUG) == 0) { - int i; - int ret = 0; - - if ((i = simple_strtoul(page, NULL, 10)) != 0) { - unsigned long flags; - - save_flags(flags); cli(); - if (ch->debug_area) kfree(ch->debug_area); - if ((ch->debug_area = kmalloc(ch->debug_size = i, - GFP_KERNEL)) == NULL) { - ret = -ENOMEM; - } - ch->debug_start = ch->debug_end = 0; - restore_flags(flags); - free_page((unsigned long)page); - return ret ? ret : count; - } - - if (*page != '+' && *page != '-') { - free_page((unsigned long)page); - return -EINVAL; - } - while (comx_debugflags[i].value && - strncmp(comx_debugflags[i].name, page + 1, - strlen(comx_debugflags[i].name))) { - i++; - } - - if (comx_debugflags[i].value == 0) { - printk(KERN_ERR "Invalid debug option\n"); - free_page((unsigned long)page); - return -EINVAL; - } - if (*page == '+') { - ch->debug_flags |= comx_debugflags[i].value; - } else { - ch->debug_flags &= ~comx_debugflags[i].value; - } - } else if (strcmp(entry->name, FILENAME_HARDWARE) == 0) { - if(strlen(page)>10) { - free_page((unsigned long)page); - return -EINVAL; - } - while (hw) { - if (strcmp(hw->name, page) == 0) { - break; - } else { - hw = hw->next; - } - } -#ifdef CONFIG_KMOD - if(!hw && comx_strcasecmp(HWNAME_NONE,page) != 0){ - request_module("comx-hw-%s",page); - } - hw=comx_channels; - while (hw) { - if (comx_strcasecmp(hw->name, page) == 0) { - break; - } else { - hw = hw->next; - } - } -#endif - - if (comx_strcasecmp(HWNAME_NONE, page) != 0 && !hw) { - free_page((unsigned long)page); - return -ENODEV; - } - if (ch->init_status & HW_OPEN) { - free_page((unsigned long)page); - return -EBUSY; - } - if (ch->hardware && ch->hardware->hw_exit && - (ret=ch->hardware->hw_exit(dev))) { - free_page((unsigned long)page); - return ret; - } - ch->hardware = hw; - entry->size = strlen(page) + 1; - if (hw && hw->hw_init) hw->hw_init(dev); - } else if (strcmp(entry->name, FILENAME_PROTOCOL) == 0) { - if(strlen(page)>10) { - free_page((unsigned long)page); - return -EINVAL; - } - while (line) { - if (comx_strcasecmp(line->name, page) == 0) { - break; - } else { - line = line->next; - } - } -#ifdef CONFIG_KMOD - if(!line && comx_strcasecmp(PROTONAME_NONE, page) != 0) { - request_module("comx-proto-%s",page); - } - line=comx_lines; - while (line) { - if (comx_strcasecmp(line->name, page) == 0) { - break; - } else { - line = line->next; - } - } -#endif - - if (comx_strcasecmp(PROTONAME_NONE, page) != 0 && !line) { - free_page((unsigned long)page); - return -ENODEV; - } - - if (ch->init_status & LINE_OPEN) { - free_page((unsigned long)page); - return -EBUSY; - } - - if (ch->protocol && ch->protocol->line_exit && - (ret=ch->protocol->line_exit(dev))) { - free_page((unsigned long)page); - return ret; - } - ch->protocol = line; - entry->size = strlen(page) + 1; - comx_reset_dev(dev); - if (line && line->line_init) line->line_init(dev); - } else if (strcmp(entry->name, FILENAME_LINEUPDELAY) == 0) { - int i; - - if ((i = simple_strtoul(page, NULL, 10)) != 0) { - if (i >=0 && i < 10) { - ch->lineup_delay = i; - } else { - printk(KERN_ERR "comx: invalid lineup_delay value\n"); - } - } - } -out: - free_page((unsigned long)page); - return count; -} - -static int comx_mkdir(struct inode *dir, struct dentry *dentry, int mode) -{ - struct proc_dir_entry *new_dir, *debug_file; - struct net_device *dev; - struct comx_channel *ch; - int ret = -EIO; - - if ((dev = kmalloc(sizeof(struct net_device), GFP_KERNEL)) == NULL) { - return -ENOMEM; - } - memset(dev, 0, sizeof(struct net_device)); - - lock_kernel(); - if ((new_dir = create_proc_entry(dentry->d_name.name, mode | S_IFDIR, - comx_root_dir)) == NULL) { - goto cleanup_dev; - } - - new_dir->nlink = 2; - new_dir->data = NULL; // ide jon majd a struct dev - - /* Ezek kellenek */ - if (!create_comx_proc_entry(FILENAME_HARDWARE, 0644, - strlen(HWNAME_NONE) + 1, new_dir)) { - goto cleanup_new_dir; - } - if (!create_comx_proc_entry(FILENAME_PROTOCOL, 0644, - strlen(PROTONAME_NONE) + 1, new_dir)) { - goto cleanup_filename_hardware; - } - if (!create_comx_proc_entry(FILENAME_STATUS, 0444, 0, new_dir)) { - goto cleanup_filename_protocol; - } - if (!create_comx_proc_entry(FILENAME_LINEUPDELAY, 0644, 2, new_dir)) { - goto cleanup_filename_status; - } - - if ((debug_file = create_proc_entry(FILENAME_DEBUG, - S_IFREG | 0644, new_dir)) == NULL) { - goto cleanup_filename_lineupdelay; - } - debug_file->data = (void *)debug_file; - debug_file->read_proc = NULL; // see below - debug_file->write_proc = &comx_write_proc; - debug_file->nlink = 1; - - strcpy(dev->name, (char *)new_dir->name); - dev->init = comx_init_dev; - - if (register_netdevice(dev)) { - goto cleanup_filename_debug; - } - ch = dev->priv; - if((ch->if_ptr = (void *)kmalloc(sizeof(struct ppp_device), - GFP_KERNEL)) == NULL) { - goto cleanup_register; - } - memset(ch->if_ptr, 0, sizeof(struct ppp_device)); - ch->debug_file = debug_file; - ch->procdir = new_dir; - new_dir->data = dev; - - ch->debug_start = ch->debug_end = 0; - if ((ch->debug_area = kmalloc(ch->debug_size = DEFAULT_DEBUG_SIZE, - GFP_KERNEL)) == NULL) { - ret = -ENOMEM; - goto cleanup_if_ptr; - } - - ch->lineup_delay = DEFAULT_LINEUP_DELAY; - - MOD_INC_USE_COUNT; - unlock_kernel(); - return 0; -cleanup_if_ptr: - kfree(ch->if_ptr); -cleanup_register: - unregister_netdevice(dev); -cleanup_filename_debug: - remove_proc_entry(FILENAME_DEBUG, new_dir); -cleanup_filename_lineupdelay: - remove_proc_entry(FILENAME_LINEUPDELAY, new_dir); -cleanup_filename_status: - remove_proc_entry(FILENAME_STATUS, new_dir); -cleanup_filename_protocol: - remove_proc_entry(FILENAME_PROTOCOL, new_dir); -cleanup_filename_hardware: - remove_proc_entry(FILENAME_HARDWARE, new_dir); -cleanup_new_dir: - remove_proc_entry(dentry->d_name.name, comx_root_dir); -cleanup_dev: - kfree(dev); - unlock_kernel(); - return ret; -} - -static int comx_rmdir(struct inode *dir, struct dentry *dentry) -{ - struct proc_dir_entry *entry = PDE(dentry->d_inode); - struct net_device *dev; - struct comx_channel *ch; - int ret; - - lock_kernel(); - dev = entry->data; - ch = dev->priv; - if (dev->flags & IFF_UP) { - printk(KERN_ERR "%s: down interface before removing it\n", dev->name); - unlock_kernel(); - return -EBUSY; - } - - if (ch->protocol && ch->protocol->line_exit && - (ret=ch->protocol->line_exit(dev))) { - unlock_kernel(); - return ret; - } - if (ch->hardware && ch->hardware->hw_exit && - (ret=ch->hardware->hw_exit(dev))) { - if(ch->protocol && ch->protocol->line_init) { - ch->protocol->line_init(dev); - } - unlock_kernel(); - return ret; - } - ch->protocol = NULL; - ch->hardware = NULL; - - del_timer(&ch->loadavg_timer); - kfree(ch->avg_bytes); - - unregister_netdev(dev); - if (ch->debug_area) { - kfree(ch->debug_area); - } - if (dev->priv) { - kfree(dev->priv); - } - free_netdev(dev); - - remove_proc_entry(FILENAME_DEBUG, entry); - remove_proc_entry(FILENAME_LINEUPDELAY, entry); - remove_proc_entry(FILENAME_STATUS, entry); - remove_proc_entry(FILENAME_HARDWARE, entry); - remove_proc_entry(FILENAME_PROTOCOL, entry); - remove_proc_entry(dentry->d_name.name, comx_root_dir); - - MOD_DEC_USE_COUNT; - unlock_kernel(); - return 0; -} - -static struct dentry *comx_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) -{ - struct proc_dir_entry *de; - struct inode *inode = NULL; - - lock_kernel(); - if ((de = PDE(dir)) != NULL) { - for (de = de->subdir ; de ; de = de->next) { - if ((de->namelen == dentry->d_name.len) && - (memcmp(dentry->d_name.name, de->name, - de->namelen) == 0)) { - if ((inode = proc_get_inode(dir->i_sb, - de->low_ino, de)) == NULL) { - printk(KERN_ERR "COMX: lookup error\n"); - unlock_kernel(); - return ERR_PTR(-EINVAL); - } - break; - } - } - } - unlock_kernel(); - dentry->d_op = &comx_dentry_operations; - d_add(dentry, inode); - return NULL; -} - -int comx_strcasecmp(const char *cs, const char *ct) -{ - register signed char __res; - - while (1) { - if ((__res = toupper(*cs) - toupper(*ct++)) != 0 || !*cs++) { - break; - } - } - return __res; -} - -static int comx_delete_dentry(struct dentry *dentry) -{ - return 1; -} - -static struct proc_dir_entry *create_comx_proc_entry(char *name, int mode, - int size, struct proc_dir_entry *dir) -{ - struct proc_dir_entry *new_file; - - if ((new_file = create_proc_entry(name, S_IFREG | mode, dir)) != NULL) { - new_file->data = (void *)new_file; - new_file->read_proc = &comx_read_proc; - new_file->write_proc = &comx_write_proc; - new_file->size = size; - new_file->nlink = 1; - } - return(new_file); -} - -int comx_register_hardware(struct comx_hardware *comx_hw) -{ - struct comx_hardware *hw = comx_channels; - - if (!hw) { - comx_channels = comx_hw; - } else { - while (hw->next != NULL && strcmp(comx_hw->name, hw->name) != 0) { - hw = hw->next; - } - if (strcmp(comx_hw->name, hw->name) == 0) { - return -1; - } - hw->next = comx_hw; - } - - printk(KERN_INFO "COMX: driver for hardware type %s, version %s\n", comx_hw->name, comx_hw->version); - return 0; -} - -int comx_unregister_hardware(char *name) -{ - struct comx_hardware *hw = comx_channels; - - if (!hw) { - return -1; - } - - if (strcmp(hw->name, name) == 0) { - comx_channels = comx_channels->next; - return 0; - } - - while (hw->next != NULL && strcmp(hw->next->name,name) != 0) { - hw = hw->next; - } - - if (hw->next != NULL && strcmp(hw->next->name, name) == 0) { - hw->next = hw->next->next; - return 0; - } - return -1; -} - -int comx_register_protocol(struct comx_protocol *comx_line) -{ - struct comx_protocol *pr = comx_lines; - - if (!pr) { - comx_lines = comx_line; - } else { - while (pr->next != NULL && strcmp(comx_line->name, pr->name) !=0) { - pr = pr->next; - } - if (strcmp(comx_line->name, pr->name) == 0) { - return -1; - } - pr->next = comx_line; - } - - printk(KERN_INFO "COMX: driver for protocol type %s, version %s\n", comx_line->name, comx_line->version); - return 0; -} - -int comx_unregister_protocol(char *name) -{ - struct comx_protocol *pr = comx_lines; - - if (!pr) { - return -1; - } - - if (strcmp(pr->name, name) == 0) { - comx_lines = comx_lines->next; - return 0; - } - - while (pr->next != NULL && strcmp(pr->next->name,name) != 0) { - pr = pr->next; - } - - if (pr->next != NULL && strcmp(pr->next->name, name) == 0) { - pr->next = pr->next->next; - return 0; - } - return -1; -} - -static int __init comx_init(void) -{ - struct proc_dir_entry *new_file; - - comx_root_dir = create_proc_entry("comx", - S_IFDIR | S_IWUSR | S_IRUGO | S_IXUGO, &proc_root); - if (!comx_root_dir) - return -ENOMEM; - comx_root_dir->proc_iops = &comx_root_inode_ops; - - if ((new_file = create_proc_entry(FILENAME_HARDWARELIST, - S_IFREG | 0444, comx_root_dir)) == NULL) { - return -ENOMEM; - } - - new_file->data = new_file; - new_file->read_proc = &comx_root_read_proc; - new_file->write_proc = NULL; - new_file->nlink = 1; - - if ((new_file = create_proc_entry(FILENAME_PROTOCOLLIST, - S_IFREG | 0444, comx_root_dir)) == NULL) { - return -ENOMEM; - } - - new_file->data = new_file; - new_file->read_proc = &comx_root_read_proc; - new_file->write_proc = NULL; - new_file->nlink = 1; - - - printk(KERN_INFO "COMX: driver version %s (C) 1995-1999 ITConsult-Pro Co. \n", - VERSION); - return 0; -} - -static void __exit comx_exit(void) -{ - remove_proc_entry(FILENAME_HARDWARELIST, comx_root_dir); - remove_proc_entry(FILENAME_PROTOCOLLIST, comx_root_dir); - remove_proc_entry(comx_root_dir->name, &proc_root); -} - -module_init(comx_init); -module_exit(comx_exit); - -EXPORT_SYMBOL(comx_register_hardware); -EXPORT_SYMBOL(comx_unregister_hardware); -EXPORT_SYMBOL(comx_register_protocol); -EXPORT_SYMBOL(comx_unregister_protocol); -EXPORT_SYMBOL(comx_debug_skb); -EXPORT_SYMBOL(comx_debug_bytes); -EXPORT_SYMBOL(comx_debug); -EXPORT_SYMBOL(comx_lineup_func); -EXPORT_SYMBOL(comx_status); -EXPORT_SYMBOL(comx_rx); -EXPORT_SYMBOL(comx_strcasecmp); -EXPORT_SYMBOL(comx_root_dir); diff --git a/drivers/net/wan/comx.h b/drivers/net/wan/comx.h deleted file mode 100644 index 0f7404f21..000000000 --- a/drivers/net/wan/comx.h +++ /dev/null @@ -1,232 +0,0 @@ -/* - * General definitions for the COMX driver - * - * Original authors: Arpad Bakay , - * Peter Bajan , - * Previous maintainer: Tivadar Szemethy - * Currently maintained by: Gergely Madarasz - * - * Copyright (C) 1995-1999 ITConsult-Pro Co. - * - * 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. - * - * - * net_device_stats: - * rx_length_errors rec_len < 4 || rec_len > 2000 - * rx_over_errors receive overrun (OVR) - * rx_crc_errors rx crc error - * rx_frame_errors aborts rec'd (ABO) - * rx_fifo_errors status fifo overrun (PBUFOVR) - * rx_missed_errors receive buffer overrun (BUFOVR) - * tx_aborted_errors ? - * tx_carrier_errors modem line status changes - * tx_fifo_errors tx underrun (locomx) - */ -#include - -struct comx_protocol { - char *name; - char *version; - unsigned short encap_type; - int (*line_init)(struct net_device *dev); - int (*line_exit)(struct net_device *dev); - struct comx_protocol *next; - }; - -struct comx_hardware { - char *name; - char *version; - int (*hw_init)(struct net_device *dev); - int (*hw_exit)(struct net_device *dev); - int (*hw_dump)(struct net_device *dev); - struct comx_hardware *next; - }; - -struct comx_channel { - void *if_ptr; // General purpose pointer - struct net_device *dev; // Where we belong to - struct net_device *twin; // On dual-port cards - struct proc_dir_entry *procdir; // the directory - - unsigned char init_status; - unsigned char line_status; - - struct timer_list lineup_timer; // against line jitter - long int lineup_pending; - unsigned char lineup_delay; - -#if 0 - struct timer_list reset_timer; // for board resetting - long reset_pending; - int reset_timeout; -#endif - - struct net_device_stats stats; - struct net_device_stats *current_stats; -#if 0 - unsigned long board_resets; -#endif - unsigned long *avg_bytes; - int loadavg_counter, loadavg_size; - int loadavg[3]; - struct timer_list loadavg_timer; - int debug_flags; - char *debug_area; - int debug_start, debug_end, debug_size; - struct proc_dir_entry *debug_file; -#ifdef CONFIG_COMX_DEBUG_RAW - char *raw; - int raw_len; -#endif - // LINE specific - struct comx_protocol *protocol; - void (*LINE_rx)(struct net_device *dev, struct sk_buff *skb); - int (*LINE_tx)(struct net_device *dev); - void (*LINE_status)(struct net_device *dev, u_short status); - int (*LINE_open)(struct net_device *dev); - int (*LINE_close)(struct net_device *dev); - int (*LINE_xmit)(struct sk_buff *skb, struct net_device *dev); - int (*LINE_header)(struct sk_buff *skb, struct net_device *dev, - u_short type,void *daddr, void *saddr, - unsigned len); - int (*LINE_rebuild_header)(struct sk_buff *skb); - int (*LINE_statistics)(struct net_device *dev, char *page); - int (*LINE_parameter_check)(struct net_device *dev); - int (*LINE_ioctl)(struct net_device *dev, struct ifreq *ifr, - int cmd); - void (*LINE_mod_use)(int); - void * LINE_privdata; - - // HW specific - - struct comx_hardware *hardware; - void (*HW_board_on)(struct net_device *dev); - void (*HW_board_off)(struct net_device *dev); - struct net_device *(*HW_access_board)(struct net_device *dev); - void (*HW_release_board)(struct net_device *dev, struct net_device *savep); - int (*HW_txe)(struct net_device *dev); - int (*HW_open)(struct net_device *dev); - int (*HW_close)(struct net_device *dev); - int (*HW_send_packet)(struct net_device *dev,struct sk_buff *skb); - int (*HW_statistics)(struct net_device *dev, char *page); -#if 0 - int (*HW_reset)(struct net_device *dev, char *page); -#endif - int (*HW_load_board)(struct net_device *dev); - void (*HW_set_clock)(struct net_device *dev); - void *HW_privdata; - }; - -struct comx_debugflags_struct { - char *name; - int value; - }; - -#define COMX_ROOT_DIR_NAME "comx" - -#define FILENAME_HARDWARE "boardtype" -#define FILENAME_HARDWARELIST "boardtypes" -#define FILENAME_PROTOCOL "protocol" -#define FILENAME_PROTOCOLLIST "protocols" -#define FILENAME_DEBUG "debug" -#define FILENAME_CLOCK "clock" -#define FILENAME_STATUS "status" -#define FILENAME_IO "io" -#define FILENAME_IRQ "irq" -#define FILENAME_KEEPALIVE "keepalive" -#define FILENAME_LINEUPDELAY "lineup_delay" -#define FILENAME_CHANNEL "channel" -#define FILENAME_FIRMWARE "firmware" -#define FILENAME_MEMADDR "memaddr" -#define FILENAME_TWIN "twin" -#define FILENAME_T1 "t1" -#define FILENAME_T2 "t2" -#define FILENAME_N2 "n2" -#define FILENAME_WINDOW "window" -#define FILENAME_MODE "mode" -#define FILENAME_DLCI "dlci" -#define FILENAME_MASTER "master" -#ifdef CONFIG_COMX_DEBUG_RAW -#define FILENAME_RAW "raw" -#endif - -#define PROTONAME_NONE "none" -#define HWNAME_NONE "none" -#define KEEPALIVE_OFF "off" - -#define FRAME_ACCEPTED 0 /* sending and xmitter busy */ -#define FRAME_DROPPED 1 -#define FRAME_ERROR 2 /* xmitter error */ -#define FRAME_QUEUED 3 /* sending but more can come */ - -#define LINE_UP 1 /* Modem UP */ -#define PROTO_UP 2 -#define PROTO_LOOP 4 - -#define HW_OPEN 1 -#define LINE_OPEN 2 -#define FW_LOADED 4 -#define IRQ_ALLOCATED 8 - -#define DEBUG_COMX_RX 2 -#define DEBUG_COMX_TX 4 -#define DEBUG_HW_TX 16 -#define DEBUG_HW_RX 32 -#define DEBUG_HDLC_KEEPALIVE 64 -#define DEBUG_COMX_PPP 128 -#define DEBUG_COMX_LAPB 256 -#define DEBUG_COMX_DLCI 512 - -#define DEBUG_PAGESIZE 3072 -#define DEFAULT_DEBUG_SIZE 4096 -#define DEFAULT_LINEUP_DELAY 1 -#define FILE_PAGESIZE 3072 - -#ifndef COMX_PPP_MAJOR -#define COMX_PPP_MAJOR 88 -#endif - - -#define COMX_CHANNEL(dev) ((struct comx_channel*)dev->priv) - -#define TWIN(dev) (COMX_CHANNEL(dev)->twin) - - -#ifndef byte -typedef u8 byte; -#endif -#ifndef word -typedef u16 word; -#endif - -#ifndef SEEK_SET -#define SEEK_SET 0 -#endif -#ifndef SEEK_CUR -#define SEEK_CUR 1 -#endif -#ifndef SEEK_END -#define SEEK_END 2 -#endif - -extern struct proc_dir_entry * comx_root_dir; - -extern int comx_register_hardware(struct comx_hardware *comx_hw); -extern int comx_unregister_hardware(char *name); -extern int comx_register_protocol(struct comx_protocol *comx_line); -extern int comx_unregister_protocol(char *name); - -extern int comx_rx(struct net_device *dev, struct sk_buff *skb); -extern void comx_status(struct net_device *dev, int status); -extern void comx_lineup_func(unsigned long d); - -extern int comx_debug(struct net_device *dev, char *fmt, ...); -extern int comx_debug_skb(struct net_device *dev, struct sk_buff *skb, char *msg); -extern int comx_debug_bytes(struct net_device *dev, unsigned char *bytes, int len, - char *msg); -extern int comx_strcasecmp(const char *cs, const char *ct); - -extern struct inode_operations comx_normal_inode_ops; diff --git a/drivers/net/wan/comxhw.h b/drivers/net/wan/comxhw.h deleted file mode 100644 index 15230dc1f..000000000 --- a/drivers/net/wan/comxhw.h +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Defines for comxhw.c - * - * Original authors: Arpad Bakay , - * Peter Bajan , - * Previous maintainer: Tivadar Szemethy - * Current maintainer: Gergely Madarasz - * - * Copyright (C) 1995-1999 ITConsult-Pro Co. - * - * 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. - * - */ - -#define LOCOMX_IO_EXTENT 8 -#define COMX_IO_EXTENT 4 -#define HICOMX_IO_EXTENT 16 - -#define COMX_MAX_TX_SIZE 1600 -#define COMX_MAX_RX_SIZE 2048 - -#define COMX_JAIL_OFFSET 0xffff -#define COMX_JAIL_VALUE 0xfe -#define COMX_MEMORY_SIZE 65536 -#define HICOMX_MEMORY_SIZE 16384 -#define COMX_MEM_MIN 0xa0000 -#define COMX_MEM_MAX 0xf0000 - -#define COMX_DEFAULT_IO 0x360 -#define COMX_DEFAULT_IRQ 10 -#define COMX_DEFAULT_MEMADDR 0xd0000 -#define HICOMX_DEFAULT_IO 0x320 -#define HICOMX_DEFAULT_IRQ 10 -#define HICOMX_DEFAULT_MEMADDR 0xd0000 -#define LOCOMX_DEFAULT_IO 0x368 -#define LOCOMX_DEFAULT_IRQ 7 - -#define MAX_CHANNELNO 2 - -#define COMX_CHANNEL_OFFSET 0x2000 - -#define COMX_ENABLE_BOARD_IT 0x40 -#define COMX_BOARD_RESET 0x20 -#define COMX_ENABLE_BOARD_MEM 0x10 -#define COMX_DISABLE_BOARD_MEM 0 -#define COMX_DISABLE_ALL 0x00 - -#define HICOMX_DISABLE_ALL 0x00 -#define HICOMX_ENABLE_BOARD_MEM 0x02 -#define HICOMX_DISABLE_BOARD_MEM 0x0 -#define HICOMX_BOARD_RESET 0x01 -#define HICOMX_PRG_MEM 4 -#define HICOMX_DATA_MEM 0 -#define HICOMX_ID_BYTE 0x55 - -#define CMX_ID_BYTE 0x31 -#define COMX_CLOCK_CONST 8000 - -#define LINKUP_READY 3 - -#define OFF_FW_L1_ID 0x01e /* ID bytes */ -#define OFF_FW_L2_ID 0x1006 -#define FW_L1_ID_1 0xab -#define FW_L1_ID_2_COMX 0xc0 -#define FW_L1_ID_2_HICOMX 0xc1 -#define FW_L2_ID_1 0xab - -#define OFF_A_L2_CMD 0x130 /* command register for L2 */ -#define OFF_A_L2_CMDPAR 0x131 /* command parameter byte */ -#define OFF_A_L1_STATB 0x122 /* stat. block for L1 */ -#define OFF_A_L1_ABOREC 0x122 /* receive ABORT counter */ -#define OFF_A_L1_OVERRUN 0x123 /* receive overrun counter */ -#define OFF_A_L1_CRCREC 0x124 /* CRC error counter */ -#define OFF_A_L1_BUFFOVR 0x125 /* buffer overrun counter */ -#define OFF_A_L1_PBUFOVR 0x126 /* priority buffer overrun counter */ -#define OFF_A_L1_MODSTAT 0x127 /* current state of modem ctrl lines */ -#define OFF_A_L1_STATE 0x127 /* end of stat. block for L1 */ -#define OFF_A_L1_TXPC 0x128 /* Tx counter for the PC */ -#define OFF_A_L1_TXZ80 0x129 /* Tx counter for the Z80 */ -#define OFF_A_L1_RXPC 0x12a /* Rx counter for the PC */ -#define OFF_A_L1_RXZ80 0x12b /* Rx counter for the Z80 */ -#define OFF_A_L1_REPENA 0x12c /* IT rep disable */ -#define OFF_A_L1_CHNR 0x12d /* L1 channel logical number */ -#define OFF_A_L1_CLKINI 0x12e /* Timer Const */ -#define OFF_A_L2_LINKUP 0x132 /* Linkup byte */ -#define OFF_A_L2_DAV 0x134 /* Rx DAV */ -#define OFF_A_L2_RxBUFP 0x136 /* Rx buff relative to membase */ -#define OFF_A_L2_TxEMPTY 0x138 /* Tx Empty */ -#define OFF_A_L2_TxBUFP 0x13a /* Tx Buf */ -#define OFF_A_L2_NBUFFS 0x144 /* Number of buffers to fetch */ - -#define OFF_A_L2_SABMREC 0x164 /* LAPB no. of SABMs received */ -#define OFF_A_L2_SABMSENT 0x165 /* LAPB no. of SABMs sent */ -#define OFF_A_L2_REJREC 0x166 /* LAPB no. of REJs received */ -#define OFF_A_L2_REJSENT 0x167 /* LAPB no. of REJs sent */ -#define OFF_A_L2_FRMRREC 0x168 /* LAPB no. of FRMRs received */ -#define OFF_A_L2_FRMRSENT 0x169 /* LAPB no. of FRMRs sent */ -#define OFF_A_L2_PROTERR 0x16A /* LAPB no. of protocol errors rec'd */ -#define OFF_A_L2_LONGREC 0x16B /* LAPB no. of long frames */ -#define OFF_A_L2_INVNR 0x16C /* LAPB no. of invalid N(R)s rec'd */ -#define OFF_A_L2_UNDEFFR 0x16D /* LAPB no. of invalid frames */ - -#define OFF_A_L2_T1 0x174 /* T1 timer */ -#define OFF_A_L2_ADDR 0x176 /* DCE = 1, DTE = 3 */ - -#define COMX_CMD_INIT 1 -#define COMX_CMD_EXIT 2 -#define COMX_CMD_OPEN 16 -#define COMX_CMD_CLOSE 17 - diff --git a/drivers/net/wan/falc-lh.h b/drivers/net/wan/falc-lh.h deleted file mode 100644 index e30726c82..000000000 --- a/drivers/net/wan/falc-lh.h +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Defines for comx-hw-slicecom.c - FALC-LH specific - * - * Author: Bartok Istvan - * Last modified: Mon Feb 7 20:00:38 CET 2000 - * - * :set tabstop=6 - */ - -/* - * Control register offsets on the LBI (page 90) - * use it like: - * lbi[ MODE ] = 0x34; - */ - -#define MODE 0x03 -#define IPC 0x08 -#define IMR0 0x14 /* Interrupt Mask Register 0 */ -#define IMR1 0x15 -#define IMR2 0x16 -#define IMR3 0x17 -#define IMR4 0x18 -#define IMR5 0x19 -#define FMR0 0x1a /* Framer Mode Register 0 */ -#define FMR1 0x1b -#define FMR2 0x1c -#define XSW 0x1e -#define XSP 0x1f -#define XC0 0x20 -#define XC1 0x21 -#define RC0 0x22 -#define RC1 0x23 -#define XPM0 0x24 -#define XPM1 0x25 -#define XPM2 0x26 -#define TSWM 0x27 -#define IDLE 0x29 /* Idle Code */ -#define LIM0 0x34 -#define LIM1 0x35 -#define PCD 0x36 -#define PCR 0x37 -#define LIM2 0x38 - -/* - * Status registers on the LBI (page 134) - * these are read-only, use it like: - * if( lbi[ FRS0 ] ) ... - */ - -#define FRS0 0x4c /* Framer Receive Status register 0 */ -#define FRS1 0x4d /* Framer Receive Status register 1 */ -#define FECL 0x50 /* Framing Error Counter low byte */ /* Counts FAS word receive errors */ -#define FECH 0x51 /* high byte */ -#define CVCL 0x52 /* Code Violation Counter low byte */ /* Counts bipolar and HDB3 code violations */ -#define CVCH 0x53 /* high byte */ -#define CEC1L 0x54 /* CRC4 Error Counter 1 low byte */ /* Counts CRC4 errors in the incoming stream */ -#define CEC1H 0x55 /* high byte */ -#define EBCL 0x56 /* E Bit error Counter low byte */ /* E-bits: the remote end sends them, when */ -#define EBCH 0x57 /* high byte */ /* it detected a CRC4-error */ -#define ISR0 0x68 /* Interrupt Status Register 0 */ -#define ISR1 0x69 /* Interrupt Status Register 1 */ -#define ISR2 0x6a /* Interrupt Status Register 2 */ -#define ISR3 0x6b /* Interrupt Status Register 3 */ -#define ISR5 0x6c /* Interrupt Status Register 5 */ -#define GIS 0x6e /* Global Interrupt Status Register */ -#define VSTR 0x6f /* version information */ - -/* - * Bit fields - */ - -#define FRS0_LOS (1 << 7) -#define FRS0_AIS (1 << 6) -#define FRS0_LFA (1 << 5) -#define FRS0_RRA (1 << 4) -#define FRS0_AUXP (1 << 3) -#define FRS0_NMF (1 << 2) -#define FRS0_LMFA (1 << 1) - -#define FRS1_XLS (1 << 1) -#define FRS1_XLO (1) - -#define ISR2_FAR (1 << 7) -#define ISR2_LFA (1 << 6) -#define ISR2_MFAR (1 << 5) -#define ISR2_T400MS (1 << 4) -#define ISR2_AIS (1 << 3) -#define ISR2_LOS (1 << 2) -#define ISR2_RAR (1 << 1) -#define ISR2_RA (1) - -#define ISR3_ES (1 << 7) -#define ISR3_SEC (1 << 6) -#define ISR3_LMFA16 (1 << 5) -#define ISR3_AIS16 (1 << 4) -#define ISR3_RA16 (1 << 3) -#define ISR3_API (1 << 2) -#define ISR3_RSN (1 << 1) -#define ISR3_RSP (1) - -#define ISR5_XSP (1 << 7) -#define ISR5_XSN (1 << 6) diff --git a/drivers/net/wan/hscx.h b/drivers/net/wan/hscx.h deleted file mode 100644 index 675b7b1f1..000000000 --- a/drivers/net/wan/hscx.h +++ /dev/null @@ -1,103 +0,0 @@ -#define HSCX_MTU 1600 - -#define HSCX_ISTA 0x00 -#define HSCX_MASK 0x00 -#define HSCX_STAR 0x01 -#define HSCX_CMDR 0x01 -#define HSCX_MODE 0x02 -#define HSCX_TIMR 0x03 -#define HSCX_EXIR 0x04 -#define HSCX_XAD1 0x04 -#define HSCX_RBCL 0x05 -#define HSCX_SAD2 0x05 -#define HSCX_RAH1 0x06 -#define HSCX_RSTA 0x07 -#define HSCX_RAH2 0x07 -#define HSCX_RAL1 0x08 -#define HSCX_RCHR 0x09 -#define HSCX_RAL2 0x09 -#define HSCX_XBCL 0x0a -#define HSCX_BGR 0x0b -#define HSCX_CCR2 0x0c -#define HSCX_RBCH 0x0d -#define HSCX_XBCH 0x0d -#define HSCX_VSTR 0x0e -#define HSCX_RLCR 0x0e -#define HSCX_CCR1 0x0f -#define HSCX_FIFO 0x1e - -#define HSCX_HSCX_CHOFFS 0x400 -#define HSCX_SEROFFS 0x1000 - -#define HSCX_RME 0x80 -#define HSCX_RPF 0x40 -#define HSCX_RSC 0x20 -#define HSCX_XPR 0x10 -#define HSCX_TIN 0x08 -#define HSCX_ICA 0x04 -#define HSCX_EXA 0x02 -#define HSCX_EXB 0x01 - -#define HSCX_XMR 0x80 -#define HSCX_XDU 0x40 -#define HSCX_EXE 0x40 -#define HSCX_PCE 0x20 -#define HSCX_RFO 0x10 -#define HSCX_CSC 0x08 -#define HSCX_RFS 0x04 - -#define HSCX_XDOV 0x80 -#define HSCX_XFW 0x40 -#define HSCX_XRNR 0x20 -#define HSCX_RRNR 0x10 -#define HSCX_RLI 0x08 -#define HSCX_CEC 0x04 -#define HSCX_CTS 0x02 -#define HSCX_WFA 0x01 - -#define HSCX_RMC 0x80 -#define HSCX_RHR 0x40 -#define HSCX_RNR 0x20 -#define HSCX_XREP 0x20 -#define HSCX_STI 0x10 -#define HSCX_XTF 0x08 -#define HSCX_XIF 0x04 -#define HSCX_XME 0x02 -#define HSCX_XRES 0x01 - -#define HSCX_AUTO 0x00 -#define HSCX_NONAUTO 0x40 -#define HSCX_TRANS 0x80 -#define HSCX_XTRANS 0xc0 -#define HSCX_ADM16 0x20 -#define HSCX_ADM8 0x00 -#define HSCX_TMD_EXT 0x00 -#define HSCX_TMD_INT 0x10 -#define HSCX_RAC 0x08 -#define HSCX_RTS 0x04 -#define HSCX_TLP 0x01 - -#define HSCX_VFR 0x80 -#define HSCX_RDO 0x40 -#define HSCX_CRC 0x20 -#define HSCX_RAB 0x10 - -#define HSCX_CIE 0x04 -#define HSCX_RIE 0x02 - -#define HSCX_DMA 0x80 -#define HSCX_NRM 0x40 -#define HSCX_CAS 0x20 -#define HSCX_XC 0x10 - -#define HSCX_OV 0x10 - -#define HSCX_CD 0x80 - -#define HSCX_RC 0x80 - -#define HSCX_PU 0x80 -#define HSCX_NRZ 0x00 -#define HSCX_NRZI 0x40 -#define HSCX_ODS 0x10 -#define HSCX_ITF 0x08 diff --git a/drivers/net/wan/mixcom.h b/drivers/net/wan/mixcom.h deleted file mode 100644 index 1815eef75..000000000 --- a/drivers/net/wan/mixcom.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Defines for the mixcom board - * - * Author: Gergely Madarasz - * - * Copyright (C) 1999 ITConsult-Pro Co. - * - * 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. - * - */ - -#define MIXCOM_IO_EXTENT 0x20 - -#define MIXCOM_DEFAULT_IO 0x180 -#define MIXCOM_DEFAULT_IRQ 5 - -#define MIXCOM_ID 0x11 -#define MIXCOM_SERIAL_OFFSET 0x1000 -#define MIXCOM_CHANNEL_OFFSET 0x400 -#define MIXCOM_IT_OFFSET 0xc14 -#define MIXCOM_STATUS_OFFSET 0xc14 -#define MIXCOM_ID_OFFSET 0xc10 -#define MIXCOM_ON 0x1 -#define MIXCOM_OFF 0x0 - -/* Status register bits */ - -#define MIXCOM_CTSB 0x1 -#define MIXCOM_CTSA 0x2 -#define MIXCOM_CHANNELNO 0x20 -#define MIXCOM_POWERFAIL 0x40 -#define MIXCOM_BOOT 0x80 diff --git a/drivers/net/wan/munich32x.h b/drivers/net/wan/munich32x.h deleted file mode 100644 index 8f151f2ed..000000000 --- a/drivers/net/wan/munich32x.h +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Defines for comx-hw-slicecom.c - MUNICH32X specific - * - * Author: Bartok Istvan - * Last modified: Tue Jan 11 14:27:36 CET 2000 - * - * :set tabstop=6 - */ - -#define TXBUFFER_SIZE 1536 /* Max mennyit tud a kartya hardver atvenni */ -#define RXBUFFER_SIZE (TXBUFFER_SIZE+4) /* For Rx reasons it must be a multiple of 4, and =>4 (page 265) */ - /* +4 .. see page 265, bit FE */ - /* TOD: a MODE1-be nem is ezt teszem, hanem a TXBUFFER-t, lehet hogy nem is kell? */ - -//#define PCI_VENDOR_ID_SIEMENS 0x110a -#define PCI_DEVICE_ID_SIEMENS_MUNICH32X 0x2101 - -/* - * PCI config space registers (page 120) - */ - -#define MUNICH_PCI_PCIRES 0x4c /* 0xe0000 resets the chip */ - - -/* - * MUNICH slave register offsets relative to base_address[0] (PCI BAR1) (page 181): - * offsets are in bytes, registers are u32's, so we need a >>2 for indexing - * the int[] by byte offsets. Use it like: - * - * bar1[ STAT ] = ~0L; or - * x = bar1[ STAT ]; - */ - -#define CONF (0x00 >> 2) -#define CMD (0x04 >> 2) -#define STAT (0x08 >> 2) -#define STACK (0x08 >> 2) -#define IMASK (0x0c >> 2) -#define PIQBA (0x14 >> 2) -#define PIQL (0x18 >> 2) -#define MODE1 (0x20 >> 2) -#define MODE2 (0x24 >> 2) -#define CCBA (0x28 >> 2) -#define TXPOLL (0x2c >> 2) -#define TIQBA (0x30 >> 2) -#define TIQL (0x34 >> 2) -#define RIQBA (0x38 >> 2) -#define RIQL (0x3c >> 2) -#define LCONF (0x40 >> 2) /* LBI Configuration Register */ -#define LCCBA (0x44 >> 2) /* LBI Configuration Control Block */ /* DE: lehet hogy nem is kell? */ -#define LTIQBA (0x50 >> 2) /* DE: lehet hogy nem is kell? page 210: LBI DMA Controller intq - nem hasznalunk DMA-t.. */ -#define LTIQL (0x54 >> 2) /* DE: lehet hogy nem is kell? */ -#define LRIQBA (0x58 >> 2) /* DE: lehet hogy nem is kell? */ -#define LRIQL (0x5c >> 2) /* DE: lehet hogy nem is kell? */ -#define LREG0 (0x60 >> 2) /* LBI Indirect External Configuration register 0 */ -#define LREG1 (0x64 >> 2) -#define LREG2 (0x68 >> 2) -#define LREG3 (0x6c >> 2) -#define LREG4 (0x70 >> 2) -#define LREG5 (0x74 >> 2) -#define LREG6 (0x78 >> 2) /* LBI Indirect External Configuration register 6 */ -#define LSTAT (0x7c >> 2) /* LBI Status Register */ -#define GPDIR (0x80 >> 2) /* General Purpose Bus DIRection - 0..input, 1..output */ -#define GPDATA (0x84 >> 2) /* General Purpose Bus DATA */ - - -/* - * MUNICH commands: (they go into register CMD) - */ - -#define CMD_ARPCM 0x01 /* Action Request Serial PCM Core */ -#define CMD_ARLBI 0x02 /* Action Request LBI */ - - -/* - * MUNICH event bits in the STAT, STACK, IMASK registers (page 188,189) - */ - -#define STAT_PTI (1 << 15) -#define STAT_PRI (1 << 14) -#define STAT_LTI (1 << 13) -#define STAT_LRI (1 << 12) -#define STAT_IOMI (1 << 11) -#define STAT_SSCI (1 << 10) -#define STAT_LBII (1 << 9) -#define STAT_MBI (1 << 8) - -#define STAT_TI (1 << 6) -#define STAT_TSPA (1 << 5) -#define STAT_RSPA (1 << 4) -#define STAT_LBIF (1 << 3) -#define STAT_LBIA (1 << 2) -#define STAT_PCMF (1 << 1) -#define STAT_PCMA (1) - -/* - * We do not handle these (and do not touch their STAT bits) in the interrupt loop - */ - -#define STAT_NOT_HANDLED_BY_INTERRUPT (STAT_PCMF | STAT_PCMA) - - -/* - * MUNICH MODE1/MODE2 slave register fields (page 193,196) - * these are not all masks, MODE1_XX_YY are my magic values! - */ - -#define MODE1_PCM_E1 (1 << 31) /* E1, 2.048 Mbit/sec */ -#define MODE1_TBS_4 (1 << 24) /* TBS = 4 .. no Tx bit shift */ -#define MODE1_RBS_4 (1 << 18) /* RBS = 4 .. no Rx bit shift */ -#define MODE1_REN (1 << 15) /* Rx Enable */ -#define MODE1_MFL_MY TXBUFFER_SIZE /* Maximum Frame Length */ -#define MODE1_MAGIC (MODE1_PCM_E1 | MODE1_TBS_4 | MODE1_RBS_4 | MODE1_REN | MODE1_MFL_MY) - -#define MODE2_HPOLL (1 << 8) /* Hold Poll */ -#define MODE2_SPOLL (1 << 7) /* Slow Poll */ -#define MODE2_TSF (1) /* real magic - discovered by probing :) */ -// #define MODE2_MAGIC (MODE2_TSF) -#define MODE2_MAGIC (MODE2_SPOLL | MODE2_TSF) - - -/* - * LCONF bits (page 205) - * these are not all masks, LCONF_XX_YY are my magic values! - */ - -#define LCONF_IPA (1 << 31) /* Interrupt Pass. Use 1 for FALC54 */ -#define LCONF_DCA (1 << 30) /* Disregard the int's for Channel A - DMSM does not try to handle them */ -#define LCONF_DCB (1 << 29) /* Disregard the int's for Channel B */ -#define LCONF_EBCRES (1 << 22) /* Reset LBI External Bus Controller, 0..reset, 1..normal operation */ -#define LCONF_LBIRES (1 << 21) /* Reset LBI DMSM, 0..reset, 1..normal operation */ -#define LCONF_BTYP_16DEMUX (1 << 7) /* 16-bit demultiplexed bus */ -#define LCONF_ABM (1 << 4) /* Arbitration Master */ - -/* writing LCONF_MAGIC1 followed by a LCONF_MAGIC2 into LCONF resets the EBC and DMSM: */ - -#define LCONF_MAGIC1 (LCONF_BTYP_16DEMUX | LCONF_ABM | LCONF_IPA | LCONF_DCA | LCONF_DCB) -#define LCONF_MAGIC2 (LCONF_MAGIC1 | LCONF_EBCRES | LCONF_LBIRES) - - -/* - * LREGx magic values if a FALC54 is on the LBI (page 217) - */ - -#define LREG0_MAGIC 0x00000264 -#define LREG1_MAGIC 0x6e6a6b66 -#define LREG2_MAGIC 0x00000264 -#define LREG3_MAGIC 0x6e686966 -#define LREG4_MAGIC 0x00000000 -#define LREG5_MAGIC ( (7<<27) | (3<<24) | (1<<21) | (7<<3) | (2<<9) ) - - -/* - * PCM Action Specification fields (munich_ccb_t.action_spec) - */ - -#define CCB_ACTIONSPEC_IN (1 << 15) /* init */ -#define CCB_ACTIONSPEC_ICO (1 << 14) /* init only this channel */ -#define CCB_ACTIONSPEC_RES (1 << 6) /* reset all channels */ -#define CCB_ACTIONSPEC_LOC (1 << 5) -#define CCB_ACTIONSPEC_LOOP (1 << 4) -#define CCB_ACTIONSPEC_LOOPI (1 << 3) -#define CCB_ACTIONSPEC_IA (1 << 2) - - -/* - * Interrupt Information bits in the TIQ, RIQ - */ - -#define PCM_INT_HI (1 << 12) -#define PCM_INT_FI (1 << 11) -#define PCM_INT_IFC (1 << 10) -#define PCM_INT_SF (1 << 9) -#define PCM_INT_ERR (1 << 8) -#define PCM_INT_FO (1 << 7) -#define PCM_INT_FE2 (1 << 6) - -#define PCM_INT_CHANNEL( info ) (info & 0x1F) - - -/* - * Rx status info in the rx_desc_t.status - */ - -#define RX_STATUS_SF (1 << 6) -#define RX_STATUS_LOSS (1 << 5) -#define RX_STATUS_CRCO (1 << 4) -#define RX_STATUS_NOB (1 << 3) -#define RX_STATUS_LFD (1 << 2) -#define RX_STATUS_RA (1 << 1) -#define RX_STATUS_ROF 1 diff --git a/drivers/net/wan/wanxlfw.inc b/drivers/net/wan/wanxlfw.inc deleted file mode 100644 index 73da688f9..000000000 --- a/drivers/net/wan/wanxlfw.inc +++ /dev/null @@ -1,158 +0,0 @@ -static u8 firmware[]={ -0x60,0x00,0x00,0x16,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0xB9,0x40,0x00,0x00,0x00,0x00,0x00, -0x10,0x14,0x42,0x80,0x4A,0xB0,0x09,0xB0,0x00,0x00,0x10,0x04,0x67,0x00,0x00,0x0E, -0x06,0xB0,0x40,0x00,0x00,0x00,0x09,0xB0,0x00,0x00,0x10,0x04,0x58,0x80,0x0C,0x80, -0x00,0x00,0x00,0x10,0x66,0x00,0xFF,0xDE,0x21,0xFC,0x00,0x00,0x16,0xBC,0x00,0x6C, -0x21,0xFC,0x00,0x00,0x17,0x5E,0x01,0x00,0x21,0xFC,0x00,0x00,0x16,0xDE,0x01,0x78, -0x21,0xFC,0x00,0x00,0x16,0xFE,0x01,0x74,0x21,0xFC,0x00,0x00,0x17,0x1E,0x01,0x70, -0x21,0xFC,0x00,0x00,0x17,0x3E,0x01,0x6C,0x21,0xFC,0x00,0x00,0x18,0x4C,0x02,0x00, -0x23,0xFC,0x78,0x00,0x00,0x00,0xFF,0xFC,0x15,0x48,0x33,0xFC,0x04,0x80,0xFF,0xFC, -0x10,0x26,0x33,0xFC,0x01,0x10,0xFF,0xFC,0x10,0x2A,0x23,0xFC,0x00,0xD4,0x9F,0x40, -0xFF,0xFC,0x15,0x40,0x23,0xFC,0x00,0x00,0x05,0x43,0xFF,0xF9,0x01,0x00,0x23,0xFC, -0x00,0x00,0x05,0x43,0xFF,0xF9,0x01,0x14,0x23,0xFC,0x00,0x00,0x00,0x00,0xFF,0xF9, -0x01,0x10,0x23,0xFC,0x00,0x00,0x00,0x08,0xFF,0xF9,0x01,0x24,0x23,0xFC,0x00,0x00, -0x01,0x01,0xFF,0xF9,0x01,0x28,0x00,0xB9,0x00,0x0F,0x03,0x00,0xFF,0xF9,0x00,0xE8, -0x23,0xFC,0x00,0x00,0x00,0x01,0xFF,0xF9,0x00,0xD4,0x61,0x00,0x06,0x74,0x33,0xFC, -0xFF,0xFF,0xFF,0xFC,0x15,0x52,0x42,0x79,0xFF,0xFC,0x15,0x50,0x42,0x79,0xFF,0xFC, -0x15,0x64,0x2E,0x3A,0x08,0x50,0x42,0xB9,0x00,0x00,0x19,0x54,0x4A,0x87,0x66,0x00, -0x00,0x0E,0x4E,0x72,0x22,0x00,0x46,0xFC,0x27,0x00,0x60,0x00,0xFF,0xE6,0x42,0x80, -0x42,0x86,0x08,0x07,0x00,0x04,0x67,0x00,0x00,0x0A,0x08,0x87,0x00,0x00,0x61,0x00, -0x02,0xA0,0x08,0x07,0x00,0x00,0x67,0x00,0x00,0x06,0x61,0x00,0x00,0x36,0x08,0x07, -0x00,0x08,0x67,0x00,0x00,0x06,0x61,0x00,0x02,0xB8,0x08,0x07,0x00,0x0C,0x67,0x00, -0x00,0x0A,0x61,0x00,0x04,0x94,0x61,0x00,0x03,0x60,0xE2,0x8F,0x58,0x80,0x0C,0x80, -0x00,0x00,0x00,0x10,0x66,0x00,0xFF,0xBC,0x23,0xC6,0xFF,0xF9,0x00,0xE4,0x60,0x00, -0xFF,0x92,0x20,0x70,0x09,0xB0,0x00,0x00,0x10,0x04,0x4A,0xA8,0x00,0x00,0x66,0x00, -0x02,0x4E,0x21,0x7C,0x00,0x00,0x00,0x01,0x00,0x00,0x42,0xB0,0x09,0xB0,0x00,0x00, -0x19,0x58,0x42,0xB0,0x09,0xB0,0x00,0x00,0x19,0x68,0x42,0xB0,0x09,0xB0,0x00,0x00, -0x19,0x78,0x42,0xB0,0x09,0xB0,0x00,0x00,0x19,0x88,0x22,0x39,0xFF,0xFC,0x16,0xEC, -0xC2,0xB0,0x09,0xB0,0x00,0x00,0x18,0xF2,0x0C,0xA8,0x00,0x00,0x00,0x04,0x00,0x18, -0x66,0x00,0x00,0x0E,0x82,0xB0,0x09,0xB0,0x00,0x00,0x18,0xE2,0x60,0x00,0x00,0x0A, -0x82,0xB0,0x09,0xB0,0x00,0x00,0x18,0xD2,0x23,0xC1,0xFF,0xFC,0x16,0xEC,0x00,0x70, -0x10,0x00,0x09,0xB0,0x00,0x00,0x19,0xAA,0x61,0x00,0x05,0x76,0x22,0x30,0x09,0xB0, -0x00,0x00,0x18,0x92,0x22,0x70,0x09,0xB0,0x00,0x00,0x18,0x72,0x74,0x08,0x26,0x3C, -0x18,0x00,0x00,0x00,0x0C,0xA8,0x00,0x00,0x00,0x01,0x00,0x10,0x67,0x00,0x00,0x06, -0x08,0xC3,0x00,0x1A,0x22,0xC3,0x22,0xC1,0x06,0x81,0x00,0x00,0x05,0xFC,0x51,0xCA, -0xFF,0xF4,0x08,0xC3,0x00,0x1D,0x22,0xC3,0x22,0xC1,0x74,0x1C,0x22,0xFC,0x90,0x00, -0x00,0x00,0x22,0xC1,0x06,0x81,0x00,0x00,0x05,0xFC,0x51,0xCA,0xFF,0xF0,0x22,0xFC, -0xB0,0x00,0x00,0x00,0x22,0xC1,0x22,0x70,0x09,0xB0,0x00,0x00,0x18,0x62,0x24,0x70, -0x09,0xB0,0x00,0x00,0x18,0x52,0x25,0x7C,0x00,0x00,0xFF,0xFF,0x00,0x10,0x25,0x7C, -0x00,0x00,0x00,0x00,0x00,0x14,0x22,0x30,0x09,0xB0,0x00,0x00,0x18,0x72,0x33,0x41, -0x00,0x02,0x06,0x81,0x00,0x00,0x00,0x50,0x33,0x41,0x00,0x00,0x13,0x7C,0x00,0x08, -0x00,0x04,0x13,0x7C,0x00,0x08,0x00,0x05,0x0C,0xA8,0x00,0x00,0x00,0x05,0x00,0x10, -0x66,0x00,0x00,0x2A,0x42,0x6A,0x00,0x08,0x23,0x7C,0x00,0x00,0xF0,0xB8,0x00,0x34, -0x23,0x7C,0x00,0x00,0xFF,0xFF,0x00,0x38,0x33,0x7C,0x05,0xFA,0x00,0x46,0x31,0xBC, -0x00,0x02,0x09,0xB0,0x00,0x00,0x19,0x9C,0x60,0x00,0x00,0xBC,0x0C,0xA8,0x00,0x00, -0x00,0x07,0x00,0x10,0x66,0x00,0x00,0x2C,0x35,0x7C,0x08,0x00,0x00,0x08,0x23,0x7C, -0xDE,0xBB,0x20,0xE3,0x00,0x34,0x23,0x7C,0xFF,0xFF,0xFF,0xFF,0x00,0x38,0x33,0x7C, -0x05,0xFC,0x00,0x46,0x31,0xBC,0x00,0x04,0x09,0xB0,0x00,0x00,0x19,0x9C,0x60,0x00, -0x00,0x86,0x0C,0xA8,0x00,0x00,0x00,0x04,0x00,0x10,0x66,0x00,0x00,0x26,0x42,0x6A, -0x00,0x08,0x23,0x7C,0x00,0x00,0xF0,0xB8,0x00,0x34,0x42,0xA9,0x00,0x38,0x33,0x7C, -0x05,0xFA,0x00,0x46,0x31,0xBC,0x00,0x02,0x09,0xB0,0x00,0x00,0x19,0x9C,0x60,0x00, -0x00,0x56,0x0C,0xA8,0x00,0x00,0x00,0x06,0x00,0x10,0x66,0x00,0x00,0x28,0x35,0x7C, -0x08,0x00,0x00,0x08,0x23,0x7C,0xDE,0xBB,0x20,0xE3,0x00,0x34,0x42,0xA9,0x00,0x38, -0x33,0x7C,0x05,0xFC,0x00,0x46,0x31,0xBC,0x00,0x04,0x09,0xB0,0x00,0x00,0x19,0x9C, -0x60,0x00,0x00,0x24,0x42,0x6A,0x00,0x08,0x23,0x7C,0x00,0x00,0xF0,0xB8,0x00,0x34, -0x23,0x7C,0x00,0x00,0xFF,0xFF,0x00,0x38,0x33,0x7C,0x05,0xF8,0x00,0x46,0x42,0x70, -0x09,0xB0,0x00,0x00,0x19,0x9C,0x25,0x7C,0x00,0x00,0x00,0x03,0x00,0x04,0x0C,0xA8, -0x00,0x00,0x00,0x02,0x00,0x14,0x66,0x00,0x00,0x0E,0x25,0x7C,0x10,0x04,0x09,0x00, -0x00,0x00,0x60,0x00,0x00,0x0A,0x25,0x7C,0x10,0x04,0x00,0x00,0x00,0x00,0x33,0x7C, -0x05,0xFC,0x00,0x06,0x22,0x00,0xE9,0x89,0x00,0x81,0x00,0x00,0x00,0x01,0x33,0xC1, -0xFF,0xFC,0x15,0xC0,0x08,0x39,0x00,0x00,0xFF,0xFC,0x15,0xC0,0x66,0x00,0xFF,0xF6, -0x35,0x7C,0x00,0x1F,0x00,0x14,0x00,0xAA,0x00,0x00,0x00,0x30,0x00,0x00,0x4E,0x75, -0x20,0x70,0x09,0xB0,0x00,0x00,0x18,0x52,0x42,0x68,0x00,0x14,0x02,0xA8,0xFF,0xFF, -0xFF,0xCF,0x00,0x00,0x02,0x70,0xEF,0xFF,0x09,0xB0,0x00,0x00,0x19,0xAA,0x61,0x00, -0x03,0x70,0x22,0x30,0x09,0xB0,0x00,0x00,0x10,0x04,0x42,0xB0,0x19,0x90,0x4E,0x75, -0x0C,0xB0,0x00,0x00,0x00,0x0A,0x09,0xB0,0x00,0x00,0x19,0x78,0x67,0x00,0x00,0xA8, -0x22,0x30,0x09,0xB0,0x00,0x00,0x19,0x68,0x24,0x01,0x4C,0x3C,0x20,0x00,0x00,0x00, -0x00,0x0C,0xD4,0xB0,0x09,0xB0,0x00,0x00,0x10,0x04,0x06,0x82,0x00,0x00,0x00,0x1C, -0x0C,0xB0,0x00,0x00,0x00,0x10,0x29,0x90,0x66,0x00,0x00,0x7C,0x20,0x70,0x29,0xA0, -0x00,0x04,0xE7,0x89,0xD2,0xB0,0x09,0xB0,0x00,0x00,0x18,0x72,0x22,0x70,0x19,0xA0, -0x00,0x04,0x24,0x30,0x29,0xA0,0x00,0x08,0x31,0x82,0x19,0xA0,0x00,0x02,0x56,0x82, -0x02,0x82,0xFF,0xFF,0xFF,0xFC,0x23,0xC8,0xFF,0xF9,0x01,0x04,0x23,0xC9,0xFF,0xF9, -0x01,0x08,0x23,0xC2,0xFF,0xF9,0x01,0x0C,0x23,0xFC,0x00,0x00,0x01,0x03,0xFF,0xF9, -0x01,0x28,0x61,0x00,0x01,0xF6,0x08,0xF0,0x00,0x1F,0x19,0x90,0x22,0x30,0x09,0xB0, -0x00,0x00,0x19,0x68,0x52,0x81,0x0C,0x81,0x00,0x00,0x00,0x0A,0x66,0x00,0x00,0x04, -0x42,0x81,0x21,0x81,0x09,0xB0,0x00,0x00,0x19,0x68,0x52,0xB0,0x09,0xB0,0x00,0x00, -0x19,0x78,0x60,0x00,0xFF,0x4C,0x4E,0x75,0x22,0x30,0x09,0xB0,0x00,0x00,0x19,0x88, -0xE7,0x89,0xD2,0xB0,0x09,0xB0,0x00,0x00,0x18,0x82,0x34,0x30,0x19,0x90,0x08,0x02, -0x00,0x0F,0x66,0x00,0x01,0x12,0x08,0x02,0x00,0x01,0x66,0x00,0x00,0xE6,0x4A,0x70, -0x09,0xB0,0x00,0x00,0x19,0x9C,0x66,0x00,0x00,0x06,0x08,0x82,0x00,0x02,0x02,0x42, -0x0C,0xBC,0x0C,0x42,0x0C,0x00,0x66,0x00,0x00,0xDC,0x42,0x83,0x36,0x30,0x19,0xA0, -0x00,0x02,0x96,0x70,0x09,0xB0,0x00,0x00,0x19,0x9C,0x0C,0x43,0x05,0xF8,0x6E,0x00, -0x00,0xC4,0x24,0x3A,0x04,0x84,0x4C,0x3C,0x20,0x00,0x00,0x00,0x00,0x0C,0xD4,0xBA, -0xFA,0xF4,0x0C,0xB0,0x00,0x00,0x00,0x00,0x29,0x90,0x66,0x00,0x00,0x96,0x21,0x83, -0x29,0xA0,0x00,0x08,0x20,0x70,0x19,0xA0,0x00,0x04,0x22,0x70,0x29,0xA0,0x00,0x04, -0x4A,0x89,0x67,0x00,0x00,0x2A,0x56,0x83,0x02,0x83,0xFF,0xFF,0xFF,0xFC,0x23,0xC8, -0xFF,0xF9,0x01,0x1C,0x23,0xC9,0xFF,0xF9,0x01,0x18,0x23,0xC3,0xFF,0xF9,0x01,0x20, -0x23,0xFC,0x00,0x00,0x03,0x01,0xFF,0xF9,0x01,0x28,0x61,0x00,0x01,0x2C,0x21,0xB0, -0x09,0xB0,0x00,0x00,0x18,0xC2,0x29,0x90,0x08,0xC6,0x00,0x04,0x24,0x3A,0x04,0x1A, -0x52,0x82,0x0C,0x82,0x00,0x00,0x00,0x28,0x66,0x00,0x00,0x04,0x42,0x82,0x23,0xC2, -0x00,0x00,0x19,0x98,0x02,0x70,0xF0,0x00,0x19,0x90,0x08,0xF0,0x00,0x1F,0x19,0x90, -0x22,0x30,0x09,0xB0,0x00,0x00,0x19,0x88,0x52,0x81,0x0C,0x81,0x00,0x00,0x00,0x1E, -0x66,0x00,0x00,0x04,0x42,0x81,0x21,0x81,0x09,0xB0,0x00,0x00,0x19,0x88,0x60,0x00, -0xFE,0xF8,0x24,0x30,0x09,0xB0,0x00,0x00,0x10,0x04,0x52,0xB0,0x29,0xA0,0x00,0x08, -0x60,0x00,0xFF,0xC2,0x24,0x30,0x09,0xB0,0x00,0x00,0x10,0x04,0x52,0xB0,0x29,0xA0, -0x00,0x0C,0x60,0x00,0xFF,0xB0,0x4E,0x75,0x4A,0xB0,0x09,0xB0,0x00,0x00,0x19,0x78, -0x67,0x00,0x00,0x86,0x22,0x30,0x09,0xB0,0x00,0x00,0x19,0x58,0x24,0x01,0xE7,0x89, -0xD2,0xB0,0x09,0xB0,0x00,0x00,0x18,0x72,0x36,0x30,0x19,0x90,0x08,0x03,0x00,0x0F, -0x66,0x00,0x00,0x66,0x8C,0xB0,0x09,0xB0,0x00,0x00,0x18,0xA2,0x53,0xB0,0x09,0xB0, -0x00,0x00,0x19,0x78,0x22,0x30,0x09,0xB0,0x00,0x00,0x19,0x58,0x52,0x81,0x0C,0x81, -0x00,0x00,0x00,0x0A,0x66,0x00,0x00,0x04,0x42,0x81,0x21,0x81,0x09,0xB0,0x00,0x00, -0x19,0x58,0x4C,0x3C,0x20,0x00,0x00,0x00,0x00,0x0C,0xD4,0xB0,0x09,0xB0,0x00,0x00, -0x10,0x04,0x06,0x82,0x00,0x00,0x00,0x1C,0x08,0x03,0x00,0x01,0x66,0x00,0x00,0x0E, -0x21,0xBC,0x00,0x00,0x00,0x20,0x29,0x90,0x60,0x00,0xFF,0x7E,0x21,0xBC,0x00,0x00, -0x00,0x30,0x29,0x90,0x60,0x00,0xFF,0x72,0x4E,0x75,0x2F,0x00,0x40,0xE7,0x20,0x39, -0xFF,0xF9,0x01,0x28,0x08,0x00,0x00,0x04,0x66,0x00,0x00,0x2C,0x4E,0x72,0x22,0x00, -0x46,0xFC,0x27,0x00,0x60,0x00,0xFF,0xE8,0x2F,0x00,0x40,0xE7,0x20,0x39,0xFF,0xF9, -0x01,0x28,0x08,0x00,0x00,0x0C,0x66,0x00,0x00,0x0E,0x4E,0x72,0x22,0x00,0x46,0xFC, -0x27,0x00,0x60,0x00,0xFF,0xE8,0x46,0xDF,0x20,0x1F,0x4E,0x75,0x2F,0x00,0x20,0x39, -0xFF,0xF9,0x00,0xE0,0x23,0xC0,0xFF,0xF9,0x00,0xE0,0x81,0xB9,0x00,0x00,0x19,0x54, -0x23,0xFC,0x00,0x00,0x09,0x09,0xFF,0xF9,0x01,0x28,0x20,0x1F,0x4E,0x73,0x00,0xB9, -0x00,0x00,0x00,0x00,0xFF,0xFC,0x16,0x10,0x00,0xB9,0x00,0x00,0x10,0x00,0x00,0x00, -0x19,0x54,0x23,0xFC,0x40,0x00,0x00,0x00,0xFF,0xFC,0x15,0x4C,0x4E,0x73,0x00,0xB9, -0x00,0x00,0x00,0x00,0xFF,0xFC,0x16,0x30,0x00,0xB9,0x00,0x00,0x20,0x00,0x00,0x00, -0x19,0x54,0x23,0xFC,0x20,0x00,0x00,0x00,0xFF,0xFC,0x15,0x4C,0x4E,0x73,0x00,0xB9, -0x00,0x00,0x00,0x00,0xFF,0xFC,0x16,0x50,0x00,0xB9,0x00,0x00,0x40,0x00,0x00,0x00, -0x19,0x54,0x23,0xFC,0x10,0x00,0x00,0x00,0xFF,0xFC,0x15,0x4C,0x4E,0x73,0x00,0xB9, -0x00,0x00,0x00,0x00,0xFF,0xFC,0x16,0x70,0x00,0xB9,0x00,0x00,0x80,0x00,0x00,0x00, -0x19,0x54,0x23,0xFC,0x08,0x00,0x00,0x00,0xFF,0xFC,0x15,0x4C,0x4E,0x73,0x4E,0x73, -0x2F,0x00,0x2F,0x01,0x2F,0x02,0x2F,0x08,0x2F,0x09,0x42,0x80,0x20,0x7C,0xFF,0xFB, -0x00,0x00,0x32,0x10,0x02,0x81,0x00,0x00,0x00,0xE7,0x0C,0x41,0x00,0x42,0x66,0x00, -0x00,0x0A,0x32,0x3C,0x0E,0x08,0x60,0x00,0x00,0x3E,0x0C,0x41,0x00,0x63,0x66,0x00, -0x00,0x0A,0x32,0x3C,0x04,0x08,0x60,0x00,0x00,0x2E,0x0C,0x41,0x00,0x84,0x66,0x00, -0x00,0x0A,0x32,0x3C,0x02,0x08,0x60,0x00,0x00,0x1E,0x0C,0x41,0x00,0xA5,0x66,0x00, -0x00,0x0A,0x32,0x3C,0x0D,0x08,0x60,0x00,0x00,0x0E,0x32,0x3C,0x00,0x08,0x34,0x3C, -0x80,0xE7,0x60,0x00,0x00,0x14,0x34,0x30,0x09,0xB0,0x00,0x00,0x19,0xAA,0x02,0x42, -0x30,0x00,0x82,0x42,0x34,0x3C,0x80,0xFF,0xB2,0x70,0x09,0xB0,0x00,0x00,0x19,0xAC, -0x67,0x00,0x00,0x0C,0x31,0x81,0x09,0xB0,0x00,0x00,0x19,0xAC,0x30,0x81,0x32,0x39, -0xFF,0xFC,0x15,0x66,0xC2,0x70,0x09,0xB0,0x00,0x00,0x19,0x02,0x67,0x00,0x00,0x0C, -0x32,0x10,0x02,0x41,0xFF,0xF7,0x60,0x00,0x00,0x08,0x32,0x10,0x00,0x41,0x00,0x08, -0xC2,0x42,0x22,0x70,0x09,0xB0,0x00,0x00,0x10,0x04,0xB2,0xA9,0x00,0x04,0x67,0x00, -0x00,0x12,0x23,0x41,0x00,0x04,0x23,0xF0,0x09,0xB0,0x00,0x00,0x18,0xB2,0xFF,0xF9, -0x00,0xE4,0x54,0x88,0x58,0x80,0x0C,0x80,0x00,0x00,0x00,0x10,0x66,0x00,0xFF,0x34, -0x22,0x5F,0x20,0x5F,0x24,0x1F,0x22,0x1F,0x20,0x1F,0x4E,0x75,0x61,0x00,0xFF,0x12, -0x4E,0x73,0xFF,0xFC,0x16,0x00,0xFF,0xFC,0x16,0x20,0xFF,0xFC,0x16,0x40,0xFF,0xFC, -0x16,0x60,0xFF,0xFC,0x0C,0x00,0xFF,0xFC,0x0D,0x00,0xFF,0xFC,0x0E,0x00,0xFF,0xFC, -0x0F,0x00,0xFF,0xFC,0x00,0x00,0xFF,0xFC,0x01,0x40,0xFF,0xFC,0x02,0x80,0xFF,0xFC, -0x03,0xC0,0xFF,0xFC,0x00,0x50,0xFF,0xFC,0x01,0x90,0xFF,0xFC,0x02,0xD0,0xFF,0xFC, -0x04,0x10,0x00,0x00,0x40,0x00,0x00,0x01,0x2F,0x60,0x00,0x02,0x1E,0xC0,0x00,0x03, -0x0E,0x20,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x04,0x00,0x00, -0x00,0x08,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x80,0x00,0x00, -0x01,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x12,0x00,0x00, -0x00,0x13,0x00,0x00,0x00,0x2C,0x00,0x00,0x3E,0x00,0x00,0x2C,0x00,0x00,0x3E,0x00, -0x00,0x00,0x00,0x00,0x00,0x2D,0x00,0x00,0x3F,0x00,0x00,0x2D,0x00,0x00,0x3F,0x00, -0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00, -0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x80,0x00,0x00,0x02,0x00,0x00,0x00,0x08,0x00, -0x77,0x61,0x6E,0x58,0x4C,0x20,0x66,0x69,0x72,0x6D,0x77,0x61,0x72,0x65,0x0A,0x43, -0x6F,0x70,0x79,0x72,0x69,0x67,0x68,0x74,0x20,0x28,0x43,0x29,0x20,0x32,0x30,0x30, -0x33,0x20,0x4B,0x72,0x7A,0x79,0x73,0x7A,0x74,0x6F,0x66,0x20,0x48,0x61,0x6C,0x61, -0x73,0x61,0x20,0x3C,0x6B,0x68,0x63,0x40,0x70,0x6D,0x2E,0x77,0x61,0x77,0x2E,0x70, -0x6C,0x3E,0x0A,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,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,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,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -}; diff --git a/drivers/pcmcia/pxa2xx_base.h b/drivers/pcmcia/pxa2xx_base.h new file mode 100644 index 000000000..e46cff345 --- /dev/null +++ b/drivers/pcmcia/pxa2xx_base.h @@ -0,0 +1,3 @@ +/* temporary measure */ +extern int pxa2xx_drv_pcmcia_probe(struct device *); + diff --git a/drivers/pcmcia/sa1100.h b/drivers/pcmcia/sa1100.h deleted file mode 100644 index d2defe598..000000000 --- a/drivers/pcmcia/sa1100.h +++ /dev/null @@ -1,164 +0,0 @@ -/*====================================================================== - - Device driver for the PCMCIA control functionality of StrongARM - SA-1100 microprocessors. - - The contents of this file are subject to the Mozilla Public - License Version 1.1 (the "License"); you may not use this file - except in compliance with the License. You may obtain a copy of - the License at http://www.mozilla.org/MPL/ - - Software distributed under the License is distributed on an "AS - IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or - implied. See the License for the specific language governing - rights and limitations under the License. - - The initial developer of the original code is John G. Dorsey - . Portions created by John G. Dorsey are - Copyright (C) 1999 John G. Dorsey. All Rights Reserved. - - Alternatively, the contents of this file may be used under the - terms of the GNU Public License version 2 (the "GPL"), in which - case the provisions of the GPL are applicable instead of the - above. If you wish to allow the use of your version of this file - only under the terms of the GPL and not to allow others to use - your version of this file under the MPL, indicate your decision - by deleting the provisions above and replace them with the notice - and other provisions required by the GPL. If you do not delete - the provisions above, a recipient may use your version of this - file under either the MPL or the GPL. - -======================================================================*/ - -#if !defined(_PCMCIA_SA1100_H) -# define _PCMCIA_SA1100_H - -#include -#include -#include -#include -#include "cs_internal.h" -#include "sa1100_generic.h" - -/* MECR: Expansion Memory Configuration Register - * (SA-1100 Developers Manual, p.10-13; SA-1110 Developers Manual, p.10-24) - * - * MECR layout is: - * - * FAST1 BSM1<4:0> BSA1<4:0> BSIO1<4:0> FAST0 BSM0<4:0> BSA0<4:0> BSIO0<4:0> - * - * (This layout is actually true only for the SA-1110; the FASTn bits are - * reserved on the SA-1100.) - */ - -#define MECR_SOCKET_0_SHIFT (0) -#define MECR_SOCKET_1_SHIFT (16) - -#define MECR_BS_MASK (0x1f) -#define MECR_FAST_MODE_MASK (0x01) - -#define MECR_BSIO_SHIFT (0) -#define MECR_BSA_SHIFT (5) -#define MECR_BSM_SHIFT (10) -#define MECR_FAST_SHIFT (15) - -#define MECR_SET(mecr, sock, shift, mask, bs) \ -((mecr)=((mecr)&~(((mask)<<(shift))<<\ - ((sock)==0?MECR_SOCKET_0_SHIFT:MECR_SOCKET_1_SHIFT)))|\ - (((bs)<<(shift))<<((sock)==0?MECR_SOCKET_0_SHIFT:MECR_SOCKET_1_SHIFT))) - -#define MECR_GET(mecr, sock, shift, mask) \ -((((mecr)>>(((sock)==0)?MECR_SOCKET_0_SHIFT:MECR_SOCKET_1_SHIFT))>>\ - (shift))&(mask)) - -#define MECR_BSIO_SET(mecr, sock, bs) \ -MECR_SET((mecr), (sock), MECR_BSIO_SHIFT, MECR_BS_MASK, (bs)) - -#define MECR_BSIO_GET(mecr, sock) \ -MECR_GET((mecr), (sock), MECR_BSIO_SHIFT, MECR_BS_MASK) - -#define MECR_BSA_SET(mecr, sock, bs) \ -MECR_SET((mecr), (sock), MECR_BSA_SHIFT, MECR_BS_MASK, (bs)) - -#define MECR_BSA_GET(mecr, sock) \ -MECR_GET((mecr), (sock), MECR_BSA_SHIFT, MECR_BS_MASK) - -#define MECR_BSM_SET(mecr, sock, bs) \ -MECR_SET((mecr), (sock), MECR_BSM_SHIFT, MECR_BS_MASK, (bs)) - -#define MECR_BSM_GET(mecr, sock) \ -MECR_GET((mecr), (sock), MECR_BSM_SHIFT, MECR_BS_MASK) - -#define MECR_FAST_SET(mecr, sock, fast) \ -MECR_SET((mecr), (sock), MECR_FAST_SHIFT, MECR_FAST_MODE_MASK, (fast)) - -#define MECR_FAST_GET(mecr, sock) \ -MECR_GET((mecr), (sock), MECR_FAST_SHIFT, MECR_FAST_MODE_MASK) - - -/* This function implements the BS value calculation for setting the MECR - * using integer arithmetic: - */ -static inline unsigned int sa1100_pcmcia_mecr_bs(unsigned int pcmcia_cycle_ns, - unsigned int cpu_clock_khz){ - unsigned int t = ((pcmcia_cycle_ns * cpu_clock_khz) / 6) - 1000000; - return (t / 1000000) + (((t % 1000000) == 0) ? 0 : 1); -} - -/* This function returns the (approxmiate) command assertion period, in - * nanoseconds, for a given CPU clock frequency and MECR BS value: - */ -static inline unsigned int sa1100_pcmcia_cmd_time(unsigned int cpu_clock_khz, - unsigned int pcmcia_mecr_bs){ - return (((10000000 * 2) / cpu_clock_khz) * (3 * (pcmcia_mecr_bs + 1))) / 10; -} - - -/* SA-1100 PCMCIA Memory and I/O timing - * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - * The SA-1110 Developer's Manual, section 10.2.5, says the following: - * - * "To calculate the recommended BS_xx value for each address space: - * divide the command width time (the greater of twIOWR and twIORD, - * or the greater of twWE and twOE) by processor cycle time; divide - * by 2; divide again by 3 (number of BCLK's per command assertion); - * round up to the next whole number; and subtract 1." - * - * The PC Card Standard, Release 7, section 4.13.4, says that twIORD - * has a minimum value of 165ns. Section 4.13.5 says that twIOWR has - * a minimum value of 165ns, as well. Section 4.7.2 (describing - * common and attribute memory write timing) says that twWE has a - * minimum value of 150ns for a 250ns cycle time (for 5V operation; - * see section 4.7.4), or 300ns for a 600ns cycle time (for 3.3V - * operation, also section 4.7.4). Section 4.7.3 says that taOE - * has a maximum value of 150ns for a 300ns cycle time (for 5V - * operation), or 300ns for a 600ns cycle time (for 3.3V operation). - * - * When configuring memory maps, Card Services appears to adopt the policy - * that a memory access time of "0" means "use the default." The default - * PCMCIA I/O command width time is 165ns. The default PCMCIA 5V attribute - * and memory command width time is 150ns; the PCMCIA 3.3V attribute and - * memory command width time is 300ns. - */ -#define SA1100_PCMCIA_IO_ACCESS (165) -#define SA1100_PCMCIA_5V_MEM_ACCESS (150) -#define SA1100_PCMCIA_3V_MEM_ACCESS (300) - - -/* The socket driver actually works nicely in interrupt-driven form, - * so the (relatively infrequent) polling is "just to be sure." - */ -#define SA1100_PCMCIA_POLL_PERIOD (2*HZ) - -struct pcmcia_low_level; - -/* I/O pins replacing memory pins - * (PCMCIA System Architecture, 2nd ed., by Don Anderson, p.75) - * - * These signals change meaning when going from memory-only to - * memory-or-I/O interface: - */ -#define iostschg bvd1 -#define iospkr bvd2 - -#endif /* !defined(_PCMCIA_SA1100_H) */ diff --git a/drivers/pcmcia/sa11xx_core.c b/drivers/pcmcia/sa11xx_core.c deleted file mode 100644 index d7249c033..000000000 --- a/drivers/pcmcia/sa11xx_core.c +++ /dev/null @@ -1,971 +0,0 @@ -/*====================================================================== - - Device driver for the PCMCIA control functionality of StrongARM - SA-1100 microprocessors. - - The contents of this file are subject to the Mozilla Public - License Version 1.1 (the "License"); you may not use this file - except in compliance with the License. You may obtain a copy of - the License at http://www.mozilla.org/MPL/ - - Software distributed under the License is distributed on an "AS - IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or - implied. See the License for the specific language governing - rights and limitations under the License. - - The initial developer of the original code is John G. Dorsey - . Portions created by John G. Dorsey are - Copyright (C) 1999 John G. Dorsey. All Rights Reserved. - - Alternatively, the contents of this file may be used under the - terms of the GNU Public License version 2 (the "GPL"), in which - case the provisions of the GPL are applicable instead of the - above. If you wish to allow the use of your version of this file - only under the terms of the GPL and not to allow others to use - your version of this file under the MPL, indicate your decision - by deleting the provisions above and replace them with the notice - and other provisions required by the GPL. If you do not delete - the provisions above, a recipient may use your version of this - file under either the MPL or the GPL. - -======================================================================*/ -/* - * Please see linux/Documentation/arm/SA1100/PCMCIA for more information - * on the low-level kernel interface. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "sa11xx_core.h" -#include "sa1100.h" - -#ifdef DEBUG -static int pc_debug; - -module_param(pc_debug, int, 0644); - -#define debug(skt, lvl, fmt, arg...) do { \ - if (pc_debug > (lvl)) \ - printk(KERN_DEBUG "skt%u: %s: " fmt, \ - (skt)->nr, __func__ , ## arg); \ -} while (0) - -#else -#define debug(skt, lvl, fmt, arg...) do { } while (0) -#endif - -#define to_sa1100_socket(x) container_of(x, struct sa1100_pcmcia_socket, socket) - -/* - * sa1100_pcmcia_default_mecr_timing - * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - * - * Calculate MECR clock wait states for given CPU clock - * speed and command wait state. This function can be over- - * written by a board specific version. - * - * The default is to simply calculate the BS values as specified in - * the INTEL SA1100 development manual - * "Expansion Memory (PCMCIA) Configuration Register (MECR)" - * that's section 10.2.5 in _my_ version of the manual ;) - */ -static unsigned int -sa1100_pcmcia_default_mecr_timing(struct sa1100_pcmcia_socket *skt, - unsigned int cpu_speed, - unsigned int cmd_time) -{ - return sa1100_pcmcia_mecr_bs(cmd_time, cpu_speed); -} - -static unsigned short -calc_speed(unsigned short *spds, int num, unsigned short dflt) -{ - unsigned short speed = 0; - int i; - - for (i = 0; i < num; i++) - if (speed < spds[i]) - speed = spds[i]; - if (speed == 0) - speed = dflt; - - return speed; -} - -/* sa1100_pcmcia_set_mecr() - * ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - * - * set MECR value for socket based on this sockets - * io, mem and attribute space access speed. - * Call board specific BS value calculation to allow boards - * to tweak the BS values. - */ -static int -sa1100_pcmcia_set_mecr(struct sa1100_pcmcia_socket *skt, unsigned int cpu_clock) -{ - u32 mecr, old_mecr; - unsigned long flags; - unsigned short speed; - unsigned int bs_io, bs_mem, bs_attr; - - speed = calc_speed(skt->spd_io, MAX_IO_WIN, SA1100_PCMCIA_IO_ACCESS); - bs_io = skt->ops->socket_get_timing(skt, cpu_clock, speed); - - speed = calc_speed(skt->spd_mem, MAX_WIN, SA1100_PCMCIA_3V_MEM_ACCESS); - bs_mem = skt->ops->socket_get_timing(skt, cpu_clock, speed); - - speed = calc_speed(skt->spd_attr, MAX_WIN, SA1100_PCMCIA_3V_MEM_ACCESS); - bs_attr = skt->ops->socket_get_timing(skt, cpu_clock, speed); - - local_irq_save(flags); - - old_mecr = mecr = MECR; - MECR_FAST_SET(mecr, skt->nr, 0); - MECR_BSIO_SET(mecr, skt->nr, bs_io); - MECR_BSA_SET(mecr, skt->nr, bs_attr); - MECR_BSM_SET(mecr, skt->nr, bs_mem); - if (old_mecr != mecr) - MECR = mecr; - - local_irq_restore(flags); - - debug(skt, 2, "FAST %X BSM %X BSA %X BSIO %X\n", - MECR_FAST_GET(mecr, skt->nr), - MECR_BSM_GET(mecr, skt->nr), MECR_BSA_GET(mecr, skt->nr), - MECR_BSIO_GET(mecr, skt->nr)); - - return 0; -} - -static unsigned int sa1100_pcmcia_skt_state(struct sa1100_pcmcia_socket *skt) -{ - struct pcmcia_state state; - unsigned int stat; - - memset(&state, 0, sizeof(struct pcmcia_state)); - - skt->ops->socket_state(skt, &state); - - stat = state.detect ? SS_DETECT : 0; - stat |= state.ready ? SS_READY : 0; - stat |= state.wrprot ? SS_WRPROT : 0; - stat |= state.vs_3v ? SS_3VCARD : 0; - stat |= state.vs_Xv ? SS_XVCARD : 0; - - /* The power status of individual sockets is not available - * explicitly from the hardware, so we just remember the state - * and regurgitate it upon request: - */ - stat |= skt->cs_state.Vcc ? SS_POWERON : 0; - - if (skt->cs_state.flags & SS_IOCARD) - stat |= state.bvd1 ? SS_STSCHG : 0; - else { - if (state.bvd1 == 0) - stat |= SS_BATDEAD; - else if (state.bvd2 == 0) - stat |= SS_BATWARN; - } - return stat; -} - -/* - * sa1100_pcmcia_config_skt - * ^^^^^^^^^^^^^^^^^^^^^^^^ - * - * Convert PCMCIA socket state to our socket configure structure. - */ -static int -sa1100_pcmcia_config_skt(struct sa1100_pcmcia_socket *skt, socket_state_t *state) -{ - int ret; - - ret = skt->ops->configure_socket(skt, state); - if (ret == 0) { - /* - * This really needs a better solution. The IRQ - * may or may not be claimed by the driver. - */ - if (skt->irq_state != 1 && state->io_irq) { - skt->irq_state = 1; - set_irq_type(skt->irq, IRQT_FALLING); - } else if (skt->irq_state == 1 && state->io_irq == 0) { - skt->irq_state = 0; - set_irq_type(skt->irq, IRQT_NOEDGE); - } - - skt->cs_state = *state; - } - - if (ret < 0) - printk(KERN_ERR "sa1100_pcmcia: unable to configure " - "socket %d\n", skt->nr); - - return ret; -} - -/* sa1100_pcmcia_sock_init() - * ^^^^^^^^^^^^^^^^^^^^^^^^^ - * - * (Re-)Initialise the socket, turning on status interrupts - * and PCMCIA bus. This must wait for power to stabilise - * so that the card status signals report correctly. - * - * Returns: 0 - */ -static int sa1100_pcmcia_sock_init(struct pcmcia_socket *sock) -{ - struct sa1100_pcmcia_socket *skt = to_sa1100_socket(sock); - - debug(skt, 2, "initializing socket\n"); - - skt->ops->socket_init(skt); - return 0; -} - - -/* - * sa1100_pcmcia_suspend() - * ^^^^^^^^^^^^^^^^^^^^^^^ - * - * Remove power on the socket, disable IRQs from the card. - * Turn off status interrupts, and disable the PCMCIA bus. - * - * Returns: 0 - */ -static int sa1100_pcmcia_suspend(struct pcmcia_socket *sock) -{ - struct sa1100_pcmcia_socket *skt = to_sa1100_socket(sock); - int ret; - - debug(skt, 2, "suspending socket\n"); - - ret = sa1100_pcmcia_config_skt(skt, &dead_socket); - if (ret == 0) - skt->ops->socket_suspend(skt); - - return ret; -} - -static spinlock_t status_lock = SPIN_LOCK_UNLOCKED; - -/* sa1100_check_status() - * ^^^^^^^^^^^^^^^^^^^^^ - */ -static void sa1100_check_status(struct sa1100_pcmcia_socket *skt) -{ - unsigned int events; - - debug(skt, 4, "entering PCMCIA monitoring thread\n"); - - do { - unsigned int status; - unsigned long flags; - - status = sa1100_pcmcia_skt_state(skt); - - spin_lock_irqsave(&status_lock, flags); - events = (status ^ skt->status) & skt->cs_state.csc_mask; - skt->status = status; - spin_unlock_irqrestore(&status_lock, flags); - - debug(skt, 4, "events: %s%s%s%s%s%s\n", - events == 0 ? "" : "", - events & SS_DETECT ? "DETECT " : "", - events & SS_READY ? "READY " : "", - events & SS_BATDEAD ? "BATDEAD " : "", - events & SS_BATWARN ? "BATWARN " : "", - events & SS_STSCHG ? "STSCHG " : ""); - - if (events) - pcmcia_parse_events(&skt->socket, events); - } while (events); -} - -/* sa1100_pcmcia_poll_event() - * ^^^^^^^^^^^^^^^^^^^^^^^^^^ - * Let's poll for events in addition to IRQs since IRQ only is unreliable... - */ -static void sa1100_pcmcia_poll_event(unsigned long dummy) -{ - struct sa1100_pcmcia_socket *skt = (struct sa1100_pcmcia_socket *)dummy; - debug(skt, 4, "polling for events\n"); - - mod_timer(&skt->poll_timer, jiffies + SA1100_PCMCIA_POLL_PERIOD); - - sa1100_check_status(skt); -} - - -/* sa1100_pcmcia_interrupt() - * ^^^^^^^^^^^^^^^^^^^^^^^^^ - * Service routine for socket driver interrupts (requested by the - * low-level PCMCIA init() operation via sa1100_pcmcia_thread()). - * The actual interrupt-servicing work is performed by - * sa1100_pcmcia_thread(), largely because the Card Services event- - * handling code performs scheduling operations which cannot be - * executed from within an interrupt context. - */ -static irqreturn_t sa1100_pcmcia_interrupt(int irq, void *dev, struct pt_regs *regs) -{ - struct sa1100_pcmcia_socket *skt = dev; - - debug(skt, 3, "servicing IRQ %d\n", irq); - - sa1100_check_status(skt); - - return IRQ_HANDLED; -} - - -/* sa1100_pcmcia_get_status() - * ^^^^^^^^^^^^^^^^^^^^^^^^^^ - * Implements the get_status() operation for the in-kernel PCMCIA - * service (formerly SS_GetStatus in Card Services). Essentially just - * fills in bits in `status' according to internal driver state or - * the value of the voltage detect chipselect register. - * - * As a debugging note, during card startup, the PCMCIA core issues - * three set_socket() commands in a row the first with RESET deasserted, - * the second with RESET asserted, and the last with RESET deasserted - * again. Following the third set_socket(), a get_status() command will - * be issued. The kernel is looking for the SS_READY flag (see - * setup_socket(), reset_socket(), and unreset_socket() in cs.c). - * - * Returns: 0 - */ -static int -sa1100_pcmcia_get_status(struct pcmcia_socket *sock, unsigned int *status) -{ - struct sa1100_pcmcia_socket *skt = to_sa1100_socket(sock); - - skt->status = sa1100_pcmcia_skt_state(skt); - *status = skt->status; - - return 0; -} - - -/* sa1100_pcmcia_get_socket() - * ^^^^^^^^^^^^^^^^^^^^^^^^^^ - * Implements the get_socket() operation for the in-kernel PCMCIA - * service (formerly SS_GetSocket in Card Services). Not a very - * exciting routine. - * - * Returns: 0 - */ -static int -sa1100_pcmcia_get_socket(struct pcmcia_socket *sock, socket_state_t *state) -{ - struct sa1100_pcmcia_socket *skt = to_sa1100_socket(sock); - - debug(skt, 2, "\n"); - - *state = skt->cs_state; - - return 0; -} - -/* sa1100_pcmcia_set_socket() - * ^^^^^^^^^^^^^^^^^^^^^^^^^^ - * Implements the set_socket() operation for the in-kernel PCMCIA - * service (formerly SS_SetSocket in Card Services). We more or - * less punt all of this work and let the kernel handle the details - * of power configuration, reset, &c. We also record the value of - * `state' in order to regurgitate it to the PCMCIA core later. - * - * Returns: 0 - */ -static int -sa1100_pcmcia_set_socket(struct pcmcia_socket *sock, socket_state_t *state) -{ - struct sa1100_pcmcia_socket *skt = to_sa1100_socket(sock); - - debug(skt, 2, "mask: %s%s%s%s%s%sflags: %s%s%s%s%s%sVcc %d Vpp %d irq %d\n", - (state->csc_mask==0)?" ":"", - (state->csc_mask&SS_DETECT)?"DETECT ":"", - (state->csc_mask&SS_READY)?"READY ":"", - (state->csc_mask&SS_BATDEAD)?"BATDEAD ":"", - (state->csc_mask&SS_BATWARN)?"BATWARN ":"", - (state->csc_mask&SS_STSCHG)?"STSCHG ":"", - (state->flags==0)?" ":"", - (state->flags&SS_PWR_AUTO)?"PWR_AUTO ":"", - (state->flags&SS_IOCARD)?"IOCARD ":"", - (state->flags&SS_RESET)?"RESET ":"", - (state->flags&SS_SPKR_ENA)?"SPKR_ENA ":"", - (state->flags&SS_OUTPUT_ENA)?"OUTPUT_ENA ":"", - state->Vcc, state->Vpp, state->io_irq); - - return sa1100_pcmcia_config_skt(skt, state); -} /* sa1100_pcmcia_set_socket() */ - - -/* sa1100_pcmcia_set_io_map() - * ^^^^^^^^^^^^^^^^^^^^^^^^^^ - * Implements the set_io_map() operation for the in-kernel PCMCIA - * service (formerly SS_SetIOMap in Card Services). We configure - * the map speed as requested, but override the address ranges - * supplied by Card Services. - * - * Returns: 0 on success, -1 on error - */ -static int -sa1100_pcmcia_set_io_map(struct pcmcia_socket *sock, struct pccard_io_map *map) -{ - struct sa1100_pcmcia_socket *skt = to_sa1100_socket(sock); - unsigned short speed = map->speed; - - debug(skt, 2, "map %u speed %u start 0x%08x stop 0x%08x\n", - map->map, map->speed, map->start, map->stop); - debug(skt, 2, "flags: %s%s%s%s%s%s%s%s\n", - (map->flags==0)?"":"", - (map->flags&MAP_ACTIVE)?"ACTIVE ":"", - (map->flags&MAP_16BIT)?"16BIT ":"", - (map->flags&MAP_AUTOSZ)?"AUTOSZ ":"", - (map->flags&MAP_0WS)?"0WS ":"", - (map->flags&MAP_WRPROT)?"WRPROT ":"", - (map->flags&MAP_USE_WAIT)?"USE_WAIT ":"", - (map->flags&MAP_PREFETCH)?"PREFETCH ":""); - - if (map->map >= MAX_IO_WIN) { - printk(KERN_ERR "%s(): map (%d) out of range\n", __FUNCTION__, - map->map); - return -1; - } - - if (map->flags & MAP_ACTIVE) { - if (speed == 0) - speed = SA1100_PCMCIA_IO_ACCESS; - } else { - speed = 0; - } - - skt->spd_io[map->map] = speed; - sa1100_pcmcia_set_mecr(skt, cpufreq_get(0)); - - if (map->stop == 1) - map->stop = PAGE_SIZE-1; - - map->stop -= map->start; - map->stop += (unsigned long)skt->virt_io; - map->start = (unsigned long)skt->virt_io; - - return 0; -} /* sa1100_pcmcia_set_io_map() */ - - -/* sa1100_pcmcia_set_mem_map() - * ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - * Implements the set_mem_map() operation for the in-kernel PCMCIA - * service (formerly SS_SetMemMap in Card Services). We configure - * the map speed as requested, but override the address ranges - * supplied by Card Services. - * - * Returns: 0 on success, -1 on error - */ -static int -sa1100_pcmcia_set_mem_map(struct pcmcia_socket *sock, struct pccard_mem_map *map) -{ - struct sa1100_pcmcia_socket *skt = to_sa1100_socket(sock); - struct resource *res; - unsigned short speed = map->speed; - - debug(skt, 2, "map %u speed %u card_start %08x\n", - map->map, map->speed, map->card_start); - debug(skt, 2, "flags: %s%s%s%s%s%s%s%s\n", - (map->flags==0)?"":"", - (map->flags&MAP_ACTIVE)?"ACTIVE ":"", - (map->flags&MAP_16BIT)?"16BIT ":"", - (map->flags&MAP_AUTOSZ)?"AUTOSZ ":"", - (map->flags&MAP_0WS)?"0WS ":"", - (map->flags&MAP_WRPROT)?"WRPROT ":"", - (map->flags&MAP_ATTRIB)?"ATTRIB ":"", - (map->flags&MAP_USE_WAIT)?"USE_WAIT ":""); - - if (map->map >= MAX_WIN) - return -EINVAL; - - if (map->flags & MAP_ACTIVE) { - if (speed == 0) - speed = 300; - } else { - speed = 0; - } - - if (map->flags & MAP_ATTRIB) { - res = &skt->res_attr; - skt->spd_attr[map->map] = speed; - skt->spd_mem[map->map] = 0; - } else { - res = &skt->res_mem; - skt->spd_attr[map->map] = 0; - skt->spd_mem[map->map] = speed; - } - - sa1100_pcmcia_set_mecr(skt, cpufreq_get(0)); - - map->sys_stop -= map->sys_start; - map->sys_stop += res->start + map->card_start; - map->sys_start = res->start + map->card_start; - - return 0; -} - -struct bittbl { - unsigned int mask; - const char *name; -}; - -static struct bittbl status_bits[] = { - { SS_WRPROT, "SS_WRPROT" }, - { SS_BATDEAD, "SS_BATDEAD" }, - { SS_BATWARN, "SS_BATWARN" }, - { SS_READY, "SS_READY" }, - { SS_DETECT, "SS_DETECT" }, - { SS_POWERON, "SS_POWERON" }, - { SS_STSCHG, "SS_STSCHG" }, - { SS_3VCARD, "SS_3VCARD" }, - { SS_XVCARD, "SS_XVCARD" }, -}; - -static struct bittbl conf_bits[] = { - { SS_PWR_AUTO, "SS_PWR_AUTO" }, - { SS_IOCARD, "SS_IOCARD" }, - { SS_RESET, "SS_RESET" }, - { SS_DMA_MODE, "SS_DMA_MODE" }, - { SS_SPKR_ENA, "SS_SPKR_ENA" }, - { SS_OUTPUT_ENA, "SS_OUTPUT_ENA" }, -}; - -static void -dump_bits(char **p, const char *prefix, unsigned int val, struct bittbl *bits, int sz) -{ - char *b = *p; - int i; - - b += sprintf(b, "%-9s:", prefix); - for (i = 0; i < sz; i++) - if (val & bits[i].mask) - b += sprintf(b, " %s", bits[i].name); - *b++ = '\n'; - *p = b; -} - -/* show_status() - * ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - * Implements the /sys/class/pcmcia_socket/??/status file. - * - * Returns: the number of characters added to the buffer - */ -static ssize_t show_status(struct class_device *class_dev, char *buf) -{ - struct sa1100_pcmcia_socket *skt = container_of(class_dev, - struct sa1100_pcmcia_socket, socket.dev); - unsigned int clock = cpufreq_get(0); - unsigned long mecr = MECR; - char *p = buf; - - p+=sprintf(p, "slot : %d\n", skt->nr); - - dump_bits(&p, "status", skt->status, - status_bits, ARRAY_SIZE(status_bits)); - dump_bits(&p, "csc_mask", skt->cs_state.csc_mask, - status_bits, ARRAY_SIZE(status_bits)); - dump_bits(&p, "cs_flags", skt->cs_state.flags, - conf_bits, ARRAY_SIZE(conf_bits)); - - p+=sprintf(p, "Vcc : %d\n", skt->cs_state.Vcc); - p+=sprintf(p, "Vpp : %d\n", skt->cs_state.Vpp); - p+=sprintf(p, "IRQ : %d (%d)\n", skt->cs_state.io_irq, skt->irq); - - p+=sprintf(p, "I/O : %u (%u)\n", - calc_speed(skt->spd_io, MAX_IO_WIN, SA1100_PCMCIA_IO_ACCESS), - sa1100_pcmcia_cmd_time(clock, MECR_BSIO_GET(mecr, skt->nr))); - - p+=sprintf(p, "attribute: %u (%u)\n", - calc_speed(skt->spd_attr, MAX_WIN, SA1100_PCMCIA_3V_MEM_ACCESS), - sa1100_pcmcia_cmd_time(clock, MECR_BSA_GET(mecr, skt->nr))); - - p+=sprintf(p, "common : %u (%u)\n", - calc_speed(skt->spd_mem, MAX_WIN, SA1100_PCMCIA_3V_MEM_ACCESS), - sa1100_pcmcia_cmd_time(clock, MECR_BSM_GET(mecr, skt->nr))); - - return p-buf; -} -static CLASS_DEVICE_ATTR(status, S_IRUGO, show_status, NULL); - - -static struct pccard_operations sa11xx_pcmcia_operations = { - .init = sa1100_pcmcia_sock_init, - .suspend = sa1100_pcmcia_suspend, - .get_status = sa1100_pcmcia_get_status, - .get_socket = sa1100_pcmcia_get_socket, - .set_socket = sa1100_pcmcia_set_socket, - .set_io_map = sa1100_pcmcia_set_io_map, - .set_mem_map = sa1100_pcmcia_set_mem_map, -}; - -int sa11xx_request_irqs(struct sa1100_pcmcia_socket *skt, struct pcmcia_irqs *irqs, int nr) -{ - int i, res = 0; - - for (i = 0; i < nr; i++) { - if (irqs[i].sock != skt->nr) - continue; - res = request_irq(irqs[i].irq, sa1100_pcmcia_interrupt, - SA_INTERRUPT, irqs[i].str, skt); - if (res) - break; - set_irq_type(irqs[i].irq, IRQT_NOEDGE); - } - - if (res) { - printk(KERN_ERR "PCMCIA: request for IRQ%d failed (%d)\n", - irqs[i].irq, res); - - while (i--) - if (irqs[i].sock == skt->nr) - free_irq(irqs[i].irq, skt); - } - return res; -} -EXPORT_SYMBOL(sa11xx_request_irqs); - -void sa11xx_free_irqs(struct sa1100_pcmcia_socket *skt, struct pcmcia_irqs *irqs, int nr) -{ - int i; - - for (i = 0; i < nr; i++) - if (irqs[i].sock == skt->nr) - free_irq(irqs[i].irq, skt); -} -EXPORT_SYMBOL(sa11xx_free_irqs); - -void sa11xx_disable_irqs(struct sa1100_pcmcia_socket *skt, struct pcmcia_irqs *irqs, int nr) -{ - int i; - - for (i = 0; i < nr; i++) - if (irqs[i].sock == skt->nr) - set_irq_type(irqs[i].irq, IRQT_NOEDGE); -} -EXPORT_SYMBOL(sa11xx_disable_irqs); - -void sa11xx_enable_irqs(struct sa1100_pcmcia_socket *skt, struct pcmcia_irqs *irqs, int nr) -{ - int i; - - for (i = 0; i < nr; i++) - if (irqs[i].sock == skt->nr) { - set_irq_type(irqs[i].irq, IRQT_RISING); - set_irq_type(irqs[i].irq, IRQT_BOTHEDGE); - } -} -EXPORT_SYMBOL(sa11xx_enable_irqs); - -static LIST_HEAD(sa1100_sockets); -static DECLARE_MUTEX(sa1100_sockets_lock); - -static const char *skt_names[] = { - "PCMCIA socket 0", - "PCMCIA socket 1", -}; - -struct skt_dev_info { - int nskt; - struct sa1100_pcmcia_socket skt[0]; -}; - -#define SKT_DEV_INFO_SIZE(n) \ - (sizeof(struct skt_dev_info) + (n)*sizeof(struct sa1100_pcmcia_socket)) - -int sa11xx_drv_pcmcia_probe(struct device *dev, struct pcmcia_low_level *ops, int first, int nr) -{ - struct skt_dev_info *sinfo; - unsigned int cpu_clock; - int ret, i; - - /* - * set default MECR calculation if the board specific - * code did not specify one... - */ - if (!ops->socket_get_timing) - ops->socket_get_timing = sa1100_pcmcia_default_mecr_timing; - - down(&sa1100_sockets_lock); - - sinfo = kmalloc(SKT_DEV_INFO_SIZE(nr), GFP_KERNEL); - if (!sinfo) { - ret = -ENOMEM; - goto out; - } - - memset(sinfo, 0, SKT_DEV_INFO_SIZE(nr)); - sinfo->nskt = nr; - - cpu_clock = cpufreq_get(0); - - /* - * Initialise the per-socket structure. - */ - for (i = 0; i < nr; i++) { - struct sa1100_pcmcia_socket *skt = &sinfo->skt[i]; - - skt->socket.ops = &sa11xx_pcmcia_operations; - skt->socket.owner = ops->owner; - skt->socket.dev.dev = dev; - - init_timer(&skt->poll_timer); - skt->poll_timer.function = sa1100_pcmcia_poll_event; - skt->poll_timer.data = (unsigned long)skt; - skt->poll_timer.expires = jiffies + SA1100_PCMCIA_POLL_PERIOD; - - skt->nr = first + i; - skt->irq = NO_IRQ; - skt->dev = dev; - skt->ops = ops; - - skt->res_skt.start = _PCMCIA(skt->nr); - skt->res_skt.end = _PCMCIA(skt->nr) + PCMCIASp - 1; - skt->res_skt.name = skt_names[skt->nr]; - skt->res_skt.flags = IORESOURCE_MEM; - - ret = request_resource(&iomem_resource, &skt->res_skt); - if (ret) - goto out_err_1; - - skt->res_io.start = _PCMCIAIO(skt->nr); - skt->res_io.end = _PCMCIAIO(skt->nr) + PCMCIAIOSp - 1; - skt->res_io.name = "io"; - skt->res_io.flags = IORESOURCE_MEM | IORESOURCE_BUSY; - - ret = request_resource(&skt->res_skt, &skt->res_io); - if (ret) - goto out_err_2; - - skt->res_mem.start = _PCMCIAMem(skt->nr); - skt->res_mem.end = _PCMCIAMem(skt->nr) + PCMCIAMemSp - 1; - skt->res_mem.name = "memory"; - skt->res_mem.flags = IORESOURCE_MEM; - - ret = request_resource(&skt->res_skt, &skt->res_mem); - if (ret) - goto out_err_3; - - skt->res_attr.start = _PCMCIAAttr(skt->nr); - skt->res_attr.end = _PCMCIAAttr(skt->nr) + PCMCIAAttrSp - 1; - skt->res_attr.name = "attribute"; - skt->res_attr.flags = IORESOURCE_MEM; - - ret = request_resource(&skt->res_skt, &skt->res_attr); - if (ret) - goto out_err_4; - - skt->virt_io = ioremap(skt->res_io.start, 0x10000); - if (skt->virt_io == NULL) { - ret = -ENOMEM; - goto out_err_5; - } - - list_add(&skt->node, &sa1100_sockets); - - /* - * We initialize the MECR to default values here, because - * we are not guaranteed to see a SetIOMap operation at - * runtime. - */ - sa1100_pcmcia_set_mecr(skt, cpu_clock); - - ret = ops->hw_init(skt); - if (ret) - goto out_err_6; - - skt->socket.features = SS_CAP_STATIC_MAP|SS_CAP_PCCARD; - skt->socket.irq_mask = 0; - skt->socket.map_size = PAGE_SIZE; - skt->socket.pci_irq = skt->irq; - skt->socket.io_offset = (unsigned long)skt->virt_io; - - skt->status = sa1100_pcmcia_skt_state(skt); - - ret = pcmcia_register_socket(&skt->socket); - if (ret) - goto out_err_7; - - WARN_ON(skt->socket.sock != i); - - add_timer(&skt->poll_timer); - - class_device_create_file(&skt->socket.dev, &class_device_attr_status); - } - - dev_set_drvdata(dev, sinfo); - ret = 0; - goto out; - - do { - struct sa1100_pcmcia_socket *skt = &sinfo->skt[i]; - - del_timer_sync(&skt->poll_timer); - pcmcia_unregister_socket(&skt->socket); - - out_err_7: - flush_scheduled_work(); - - ops->hw_shutdown(skt); - out_err_6: - list_del(&skt->node); - iounmap(skt->virt_io); - out_err_5: - release_resource(&skt->res_attr); - out_err_4: - release_resource(&skt->res_mem); - out_err_3: - release_resource(&skt->res_io); - out_err_2: - release_resource(&skt->res_skt); - out_err_1: - i--; - } while (i > 0); - - kfree(sinfo); - - out: - up(&sa1100_sockets_lock); - return ret; -} -EXPORT_SYMBOL(sa11xx_drv_pcmcia_probe); - -int sa11xx_drv_pcmcia_remove(struct device *dev) -{ - struct skt_dev_info *sinfo = dev_get_drvdata(dev); - int i; - - dev_set_drvdata(dev, NULL); - - down(&sa1100_sockets_lock); - for (i = 0; i < sinfo->nskt; i++) { - struct sa1100_pcmcia_socket *skt = &sinfo->skt[i]; - - del_timer_sync(&skt->poll_timer); - - pcmcia_unregister_socket(&skt->socket); - - flush_scheduled_work(); - - skt->ops->hw_shutdown(skt); - - sa1100_pcmcia_config_skt(skt, &dead_socket); - - list_del(&skt->node); - iounmap(skt->virt_io); - skt->virt_io = NULL; - release_resource(&skt->res_attr); - release_resource(&skt->res_mem); - release_resource(&skt->res_io); - release_resource(&skt->res_skt); - } - up(&sa1100_sockets_lock); - - kfree(sinfo); - - return 0; -} -EXPORT_SYMBOL(sa11xx_drv_pcmcia_remove); - -#ifdef CONFIG_CPU_FREQ - -/* sa1100_pcmcia_update_mecr() - * ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - * When sa1100_pcmcia_notifier() decides that a MECR adjustment (due - * to a core clock frequency change) is needed, this routine establishes - * new BS_xx values consistent with the clock speed `clock'. - */ -static void sa1100_pcmcia_update_mecr(unsigned int clock) -{ - struct sa1100_pcmcia_socket *skt; - - down(&sa1100_sockets_lock); - list_for_each_entry(skt, &sa1100_sockets, node) - sa1100_pcmcia_set_mecr(skt, clock); - up(&sa1100_sockets_lock); -} - -/* sa1100_pcmcia_notifier() - * ^^^^^^^^^^^^^^^^^^^^^^^^ - * When changing the processor core clock frequency, it is necessary - * to adjust the MECR timings accordingly. We've recorded the timings - * requested by Card Services, so this is just a matter of finding - * out what our current speed is, and then recomputing the new MECR - * values. - * - * Returns: 0 on success, -1 on error - */ -static int -sa1100_pcmcia_notifier(struct notifier_block *nb, unsigned long val, - void *data) -{ - struct cpufreq_freqs *freqs = data; - - switch (val) { - case CPUFREQ_PRECHANGE: - if (freqs->new > freqs->old) - sa1100_pcmcia_update_mecr(freqs->new); - break; - - case CPUFREQ_POSTCHANGE: - if (freqs->new < freqs->old) - sa1100_pcmcia_update_mecr(freqs->new); - break; - } - - return 0; -} - -static struct notifier_block sa1100_pcmcia_notifier_block = { - .notifier_call = sa1100_pcmcia_notifier -}; - -static int __init sa11xx_pcmcia_init(void) -{ - int ret; - - printk(KERN_INFO "SA11xx PCMCIA\n"); - - ret = cpufreq_register_notifier(&sa1100_pcmcia_notifier_block, - CPUFREQ_TRANSITION_NOTIFIER); - if (ret < 0) - printk(KERN_ERR "Unable to register CPU frequency change " - "notifier (%d)\n", ret); - - return ret; -} -module_init(sa11xx_pcmcia_init); - -static void __exit sa11xx_pcmcia_exit(void) -{ - cpufreq_unregister_notifier(&sa1100_pcmcia_notifier_block, CPUFREQ_TRANSITION_NOTIFIER); -} - -module_exit(sa11xx_pcmcia_exit); -#endif - -MODULE_AUTHOR("John Dorsey "); -MODULE_DESCRIPTION("Linux PCMCIA Card Services: SA-11xx core socket driver"); -MODULE_LICENSE("Dual MPL/GPL"); diff --git a/drivers/pcmcia/sa11xx_core.h b/drivers/pcmcia/sa11xx_core.h deleted file mode 100644 index aadf7c0b6..000000000 --- a/drivers/pcmcia/sa11xx_core.h +++ /dev/null @@ -1,121 +0,0 @@ -/* - * linux/include/asm/arch/pcmcia.h - * - * Copyright (C) 2000 John G Dorsey - * - * This file contains definitions for the low-level SA-1100 kernel PCMCIA - * interface. Please see linux/Documentation/arm/SA1100/PCMCIA for details. - */ -#ifndef _ASM_ARCH_PCMCIA -#define _ASM_ARCH_PCMCIA - -/* include the world */ -#include -#include -#include -#include -#include -#include -#include "cs_internal.h" - -struct device; - -/* Ideally, we'd support up to MAX_SOCK sockets, but the SA-1100 only - * has support for two. This shows up in lots of hardwired ways, such - * as the fact that MECR only has enough bits to configure two sockets. - * Since it's so entrenched in the hardware, limiting the software - * in this way doesn't seem too terrible. - */ -#define SA1100_PCMCIA_MAX_SOCK (2) - -struct pcmcia_state { - unsigned detect: 1, - ready: 1, - bvd1: 1, - bvd2: 1, - wrprot: 1, - vs_3v: 1, - vs_Xv: 1; -}; - -/* - * This structure encapsulates per-socket state which we might need to - * use when responding to a Card Services query of some kind. - */ -struct sa1100_pcmcia_socket { - struct pcmcia_socket socket; - - /* - * Info from low level handler - */ - struct device *dev; - unsigned int nr; - unsigned int irq; - - /* - * Core PCMCIA state - */ - struct pcmcia_low_level *ops; - - unsigned int status; - socket_state_t cs_state; - - unsigned short spd_io[MAX_IO_WIN]; - unsigned short spd_mem[MAX_WIN]; - unsigned short spd_attr[MAX_WIN]; - - struct resource res_skt; - struct resource res_io; - struct resource res_mem; - struct resource res_attr; - void *virt_io; - - unsigned int irq_state; - - struct timer_list poll_timer; - struct list_head node; -}; - -struct pcmcia_low_level { - struct module *owner; - - int (*hw_init)(struct sa1100_pcmcia_socket *); - void (*hw_shutdown)(struct sa1100_pcmcia_socket *); - - void (*socket_state)(struct sa1100_pcmcia_socket *, struct pcmcia_state *); - int (*configure_socket)(struct sa1100_pcmcia_socket *, const socket_state_t *); - - /* - * Enable card status IRQs on (re-)initialisation. This can - * be called at initialisation, power management event, or - * pcmcia event. - */ - void (*socket_init)(struct sa1100_pcmcia_socket *); - - /* - * Disable card status IRQs and PCMCIA bus on suspend. - */ - void (*socket_suspend)(struct sa1100_pcmcia_socket *); - - /* - * Calculate MECR timing clock wait states - */ - unsigned int (*socket_get_timing)(struct sa1100_pcmcia_socket *, - unsigned int cpu_speed, unsigned int cmd_time); -}; - -struct pcmcia_irqs { - int sock; - int irq; - const char *str; -}; - -int sa11xx_request_irqs(struct sa1100_pcmcia_socket *skt, struct pcmcia_irqs *irqs, int nr); -void sa11xx_free_irqs(struct sa1100_pcmcia_socket *skt, struct pcmcia_irqs *irqs, int nr); -void sa11xx_disable_irqs(struct sa1100_pcmcia_socket *skt, struct pcmcia_irqs *irqs, int nr); -void sa11xx_enable_irqs(struct sa1100_pcmcia_socket *skt, struct pcmcia_irqs *irqs, int nr); - -extern int sa11xx_drv_pcmcia_probe(struct device *dev, struct pcmcia_low_level *ops, int first, int nr); -extern int sa11xx_drv_pcmcia_remove(struct device *dev); - -#endif diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c new file mode 100644 index 000000000..e9c5098ed --- /dev/null +++ b/drivers/scsi/ipr.c @@ -0,0 +1,6021 @@ +/* + * ipr.c -- driver for IBM Power Linux RAID adapters + * + * Written By: Brian King, IBM Corporation + * + * Copyright (C) 2003, 2004 IBM Corporation + * + * 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 + * + */ + +/* + * Notes: + * + * This driver is used to control the following SCSI adapters: + * + * IBM iSeries: 5702, 5703, 2780, 5709, 570A, 570B + * + * IBM pSeries: PCI-X Dual Channel Ultra 320 SCSI RAID Adapter + * PCI-X Dual Channel Ultra 320 SCSI Adapter + * PCI-X Dual Channel Ultra 320 SCSI RAID Enablement Card + * Embedded SCSI adapter on p615 and p655 systems + * + * Supported Hardware Features: + * - Ultra 320 SCSI controller + * - PCI-X host interface + * - Embedded PowerPC RISC Processor and Hardware XOR DMA Engine + * - Non-Volatile Write Cache + * - Supports attachment of non-RAID disks, tape, and optical devices + * - RAID Levels 0, 5, 10 + * - Hot spare + * - Background Parity Checking + * - Background Data Scrubbing + * - Ability to increase the capacity of an existing RAID 5 disk array + * by adding disks + * + * Driver Features: + * - Tagged command queuing + * - Adapter microcode download + * - PCI hot plug + * - SCSI device hot plug + * + */ + +#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 "ipr.h" + +/* + * Global Data + */ +static struct list_head ipr_ioa_head = LIST_HEAD_INIT(ipr_ioa_head); +static unsigned int ipr_log_level = IPR_DEFAULT_LOG_LEVEL; +static unsigned int ipr_max_speed = 1; +static int ipr_testmode = 0; +static spinlock_t ipr_driver_lock = SPIN_LOCK_UNLOCKED; + +/* This table describes the differences between DMA controller chips */ +static const struct ipr_chip_cfg_t ipr_chip_cfg[] = { + { /* Gemstone */ + .mailbox = 0x0042C, + .cache_line_size = 0x20, + { + .set_interrupt_mask_reg = 0x0022C, + .clr_interrupt_mask_reg = 0x00230, + .sense_interrupt_mask_reg = 0x0022C, + .clr_interrupt_reg = 0x00228, + .sense_interrupt_reg = 0x00224, + .ioarrin_reg = 0x00404, + .sense_uproc_interrupt_reg = 0x00214, + .set_uproc_interrupt_reg = 0x00214, + .clr_uproc_interrupt_reg = 0x00218 + } + }, + { /* Snipe */ + .mailbox = 0x0052C, + .cache_line_size = 0x20, + { + .set_interrupt_mask_reg = 0x00288, + .clr_interrupt_mask_reg = 0x0028C, + .sense_interrupt_mask_reg = 0x00288, + .clr_interrupt_reg = 0x00284, + .sense_interrupt_reg = 0x00280, + .ioarrin_reg = 0x00504, + .sense_uproc_interrupt_reg = 0x00290, + .set_uproc_interrupt_reg = 0x00290, + .clr_uproc_interrupt_reg = 0x00294 + } + }, +}; + +static int ipr_max_bus_speeds [] = { + IPR_80MBs_SCSI_RATE, IPR_U160_SCSI_RATE, IPR_U320_SCSI_RATE +}; + +MODULE_AUTHOR("Brian King "); +MODULE_DESCRIPTION("IBM Power RAID SCSI Adapter Driver"); +module_param_named(max_speed, ipr_max_speed, uint, 0); +MODULE_PARM_DESC(max_speed, "Maximum bus speed (0-2). Default: 1=U160. Speeds: 0=80 MB/s, 1=U160, 2=U320"); +module_param_named(log_level, ipr_log_level, uint, 0); +MODULE_PARM_DESC(log_level, "Set to 0 - 4 for increasing verbosity of device driver"); +module_param_named(testmode, ipr_testmode, int, 0); +MODULE_PARM_DESC(testmode, "DANGEROUS!!! Allows unsupported configurations"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(IPR_DRIVER_VERSION); + +static const char *ipr_gpdd_dev_end_states[] = { + "Command complete", + "Terminated by host", + "Terminated by device reset", + "Terminated by bus reset", + "Unknown", + "Command not started" +}; + +static const char *ipr_gpdd_dev_bus_phases[] = { + "Bus free", + "Arbitration", + "Selection", + "Message out", + "Command", + "Message in", + "Data out", + "Data in", + "Status", + "Reselection", + "Unknown" +}; + +/* A constant array of IOASCs/URCs/Error Messages */ +static const +struct ipr_error_table_t ipr_error_table[] = { + {0x00000000, 1, 1, + "8155: An unknown error was received"}, + {0x00330000, 0, 0, + "Soft underlength error"}, + {0x005A0000, 0, 0, + "Command to be cancelled not found"}, + {0x00808000, 0, 0, + "Qualified success"}, + {0x01080000, 1, 1, + "FFFE: Soft device bus error recovered by the IOA"}, + {0x01170600, 0, 1, + "FFF9: Device sector reassign successful"}, + {0x01170900, 0, 1, + "FFF7: Media error recovered by device rewrite procedures"}, + {0x01180200, 0, 1, + "7001: IOA sector reassignment successful"}, + {0x01180500, 0, 1, + "FFF9: Soft media error. Sector reassignment recommended"}, + {0x01180600, 0, 1, + "FFF7: Media error recovered by IOA rewrite procedures"}, + {0x01418000, 0, 1, + "FF3D: Soft PCI bus error recovered by the IOA"}, + {0x01440000, 1, 1, + "FFF6: Device hardware error recovered by the IOA"}, + {0x01448100, 0, 1, + "FFF6: Device hardware error recovered by the device"}, + {0x01448200, 1, 1, + "FF3D: Soft IOA error recovered by the IOA"}, + {0x01448300, 0, 1, + "FFFA: Undefined device response recovered by the IOA"}, + {0x014A0000, 1, 1, + "FFF6: Device bus error, message or command phase"}, + {0x015D0000, 0, 1, + "FFF6: Failure prediction threshold exceeded"}, + {0x015D9200, 0, 1, + "8009: Impending cache battery pack failure"}, + {0x02040400, 0, 0, + "34FF: Disk device format in progress"}, + {0x023F0000, 0, 0, + "Synchronization required"}, + {0x024E0000, 0, 0, + "No ready, IOA shutdown"}, + {0x02670100, 0, 1, + "3020: Storage subsystem configuration error"}, + {0x03110B00, 0, 0, + "FFF5: Medium error, data unreadable, recommend reassign"}, + {0x03110C00, 0, 0, + "7000: Medium error, data unreadable, do not reassign"}, + {0x03310000, 0, 1, + "FFF3: Disk media format bad"}, + {0x04050000, 0, 1, + "3002: Addressed device failed to respond to selection"}, + {0x04080000, 1, 1, + "3100: Device bus error"}, + {0x04080100, 0, 1, + "3109: IOA timed out a device command"}, + {0x04088000, 0, 0, + "3120: SCSI bus is not operational"}, + {0x04118000, 0, 1, + "9000: IOA reserved area data check"}, + {0x04118100, 0, 1, + "9001: IOA reserved area invalid data pattern"}, + {0x04118200, 0, 1, + "9002: IOA reserved area LRC error"}, + {0x04320000, 0, 1, + "102E: Out of alternate sectors for disk storage"}, + {0x04330000, 1, 1, + "FFF4: Data transfer underlength error"}, + {0x04338000, 1, 1, + "FFF4: Data transfer overlength error"}, + {0x043E0100, 0, 1, + "3400: Logical unit failure"}, + {0x04408500, 0, 1, + "FFF4: Device microcode is corrupt"}, + {0x04418000, 1, 1, + "8150: PCI bus error"}, + {0x04430000, 1, 0, + "Unsupported device bus message received"}, + {0x04440000, 1, 1, + "FFF4: Disk device problem"}, + {0x04448200, 1, 1, + "8150: Permanent IOA failure"}, + {0x04448300, 0, 1, + "3010: Disk device returned wrong response to IOA"}, + {0x04448400, 0, 1, + "8151: IOA microcode error"}, + {0x04448500, 0, 0, + "Device bus status error"}, + {0x04448600, 0, 1, + "8157: IOA error requiring IOA reset to recover"}, + {0x04490000, 0, 0, + "Message reject received from the device"}, + {0x04449200, 0, 1, + "8008: A permanent cache battery pack failure occurred"}, + {0x0444A000, 0, 1, + "9090: Disk unit has been modified after the last known status"}, + {0x0444A200, 0, 1, + "9081: IOA detected device error"}, + {0x0444A300, 0, 1, + "9082: IOA detected device error"}, + {0x044A0000, 1, 1, + "3110: Device bus error, message or command phase"}, + {0x04670400, 0, 1, + "9091: Incorrect hardware configuration change has been detected"}, + {0x046E0000, 0, 1, + "FFF4: Command to logical unit failed"}, + {0x05240000, 1, 0, + "Illegal request, invalid request type or request packet"}, + {0x05250000, 0, 0, + "Illegal request, invalid resource handle"}, + {0x05260000, 0, 0, + "Illegal request, invalid field in parameter list"}, + {0x05260100, 0, 0, + "Illegal request, parameter not supported"}, + {0x05260200, 0, 0, + "Illegal request, parameter value invalid"}, + {0x052C0000, 0, 0, + "Illegal request, command sequence error"}, + {0x06040500, 0, 1, + "9031: Array protection temporarily suspended, protection resuming"}, + {0x06040600, 0, 1, + "9040: Array protection temporarily suspended, protection resuming"}, + {0x06290000, 0, 1, + "FFFB: SCSI bus was reset"}, + {0x06290500, 0, 0, + "FFFE: SCSI bus transition to single ended"}, + {0x06290600, 0, 0, + "FFFE: SCSI bus transition to LVD"}, + {0x06298000, 0, 1, + "FFFB: SCSI bus was reset by another initiator"}, + {0x063F0300, 0, 1, + "3029: A device replacement has occurred"}, + {0x064C8000, 0, 1, + "9051: IOA cache data exists for a missing or failed device"}, + {0x06670100, 0, 1, + "9025: Disk unit is not supported at its physical location"}, + {0x06670600, 0, 1, + "3020: IOA detected a SCSI bus configuration error"}, + {0x06678000, 0, 1, + "3150: SCSI bus configuration error"}, + {0x06690200, 0, 1, + "9041: Array protection temporarily suspended"}, + {0x066B0200, 0, 1, + "9030: Array no longer protected due to missing or failed disk unit"}, + {0x07270000, 0, 0, + "Failure due to other device"}, + {0x07278000, 0, 1, + "9008: IOA does not support functions expected by devices"}, + {0x07278100, 0, 1, + "9010: Cache data associated with attached devices cannot be found"}, + {0x07278200, 0, 1, + "9011: Cache data belongs to devices other than those attached"}, + {0x07278400, 0, 1, + "9020: Array missing 2 or more devices with only 1 device present"}, + {0x07278500, 0, 1, + "9021: Array missing 2 or more devices with 2 or more devices present"}, + {0x07278600, 0, 1, + "9022: Exposed array is missing a required device"}, + {0x07278700, 0, 1, + "9023: Array member(s) not at required physical locations"}, + {0x07278800, 0, 1, + "9024: Array not functional due to present hardware configuration"}, + {0x07278900, 0, 1, + "9026: Array not functional due to present hardware configuration"}, + {0x07278A00, 0, 1, + "9027: Array is missing a device and parity is out of sync"}, + {0x07278B00, 0, 1, + "9028: Maximum number of arrays already exist"}, + {0x07278C00, 0, 1, + "9050: Required cache data cannot be located for a disk unit"}, + {0x07278D00, 0, 1, + "9052: Cache data exists for a device that has been modified"}, + {0x07278F00, 0, 1, + "9054: IOA resources not available due to previous problems"}, + {0x07279100, 0, 1, + "9092: Disk unit requires initialization before use"}, + {0x07279200, 0, 1, + "9029: Incorrect hardware configuration change has been detected"}, + {0x07279600, 0, 1, + "9060: One or more disk pairs are missing from an array"}, + {0x07279700, 0, 1, + "9061: One or more disks are missing from an array"}, + {0x07279800, 0, 1, + "9062: One or more disks are missing from an array"}, + {0x07279900, 0, 1, + "9063: Maximum number of functional arrays has been exceeded"}, + {0x0B260000, 0, 0, + "Aborted command, invalid descriptor"}, + {0x0B5A0000, 0, 0, + "Command terminated by host"} +}; + +static const struct ipr_ses_table_entry ipr_ses_table[] = { + { "2104-DL1 ", "XXXXXXXXXXXXXXXX", 80 }, + { "2104-TL1 ", "XXXXXXXXXXXXXXXX", 80 }, + { "HSBP07M P U2SCSI", "XXXXXXXXXXXXXXXX", 80 }, /* Hidive 7 slot */ + { "HSBP05M P U2SCSI", "XXXXXXXXXXXXXXXX", 80 }, /* Hidive 5 slot */ + { "HSBP05M S U2SCSI", "XXXXXXXXXXXXXXXX", 80 }, /* Bowtie */ + { "HSBP06E ASU2SCSI", "XXXXXXXXXXXXXXXX", 80 }, /* MartinFenning */ + { "2104-DU3 ", "XXXXXXXXXXXXXXXX", 160 }, + { "2104-TU3 ", "XXXXXXXXXXXXXXXX", 160 }, + { "HSBP04C RSU2SCSI", "XXXXXXX*XXXXXXXX", 160 }, + { "HSBP06E RSU2SCSI", "XXXXXXX*XXXXXXXX", 160 }, + { "St V1S2 ", "XXXXXXXXXXXXXXXX", 160 }, + { "HSBPD4M PU3SCSI", "XXXXXXX*XXXXXXXX", 160 }, + { "VSBPD1H U3SCSI", "XXXXXXX*XXXXXXXX", 160 } +}; + +/* + * Function Prototypes + */ +static int ipr_reset_alert(struct ipr_cmnd *); +static void ipr_process_ccn(struct ipr_cmnd *); +static void ipr_process_error(struct ipr_cmnd *); +static void ipr_reset_ioa_job(struct ipr_cmnd *); +static void ipr_initiate_ioa_reset(struct ipr_ioa_cfg *, + enum ipr_shutdown_type); + +#ifdef CONFIG_SCSI_IPR_TRACE +/** + * ipr_trc_hook - Add a trace entry to the driver trace + * @ipr_cmd: ipr command struct + * @type: trace type + * @add_data: additional data + * + * Return value: + * none + **/ +static void ipr_trc_hook(struct ipr_cmnd *ipr_cmd, + u8 type, u32 add_data) +{ + struct ipr_trace_entry *trace_entry; + struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg; + + trace_entry = &ioa_cfg->trace[ioa_cfg->trace_index++]; + trace_entry->time = jiffies; + trace_entry->op_code = ipr_cmd->ioarcb.cmd_pkt.cdb[0]; + trace_entry->type = type; + trace_entry->cmd_index = ipr_cmd->cmd_index; + trace_entry->res_handle = ipr_cmd->ioarcb.res_handle; + trace_entry->u.add_data = add_data; +} +#else +#define ipr_trc_hook(ipr_cmd, type, add_data) do { } while(0) +#endif + +/** + * ipr_reinit_ipr_cmnd - Re-initialize an IPR Cmnd block for reuse + * @ipr_cmd: ipr command struct + * + * Return value: + * none + **/ +static void ipr_reinit_ipr_cmnd(struct ipr_cmnd *ipr_cmd) +{ + struct ipr_ioarcb *ioarcb = &ipr_cmd->ioarcb; + struct ipr_ioasa *ioasa = &ipr_cmd->ioasa; + + memset(&ioarcb->cmd_pkt, 0, sizeof(struct ipr_cmd_pkt)); + ioarcb->write_data_transfer_length = 0; + ioarcb->read_data_transfer_length = 0; + ioarcb->write_ioadl_len = 0; + ioarcb->read_ioadl_len = 0; + ioasa->ioasc = 0; + ioasa->residual_data_len = 0; + + ipr_cmd->scsi_cmd = NULL; + ipr_cmd->sense_buffer[0] = 0; + ipr_cmd->dma_use_sg = 0; +} + +/** + * ipr_init_ipr_cmnd - Initialize an IPR Cmnd block + * @ipr_cmd: ipr command struct + * + * Return value: + * none + **/ +static void ipr_init_ipr_cmnd(struct ipr_cmnd *ipr_cmd) +{ + ipr_reinit_ipr_cmnd(ipr_cmd); + ipr_cmd->u.scratch = 0; + init_timer(&ipr_cmd->timer); +} + +/** + * ipr_get_free_ipr_cmnd - Get a free IPR Cmnd block + * @ioa_cfg: ioa config struct + * + * Return value: + * pointer to ipr command struct + **/ +static +struct ipr_cmnd *ipr_get_free_ipr_cmnd(struct ipr_ioa_cfg *ioa_cfg) +{ + struct ipr_cmnd *ipr_cmd; + + ipr_cmd = list_entry(ioa_cfg->free_q.next, struct ipr_cmnd, queue); + list_del(&ipr_cmd->queue); + ipr_init_ipr_cmnd(ipr_cmd); + + return ipr_cmd; +} + +/** + * ipr_unmap_sglist - Unmap scatterlist if mapped + * @ioa_cfg: ioa config struct + * @ipr_cmd: ipr command struct + * + * Return value: + * nothing + **/ +static void ipr_unmap_sglist(struct ipr_ioa_cfg *ioa_cfg, + struct ipr_cmnd *ipr_cmd) +{ + struct scsi_cmnd *scsi_cmd = ipr_cmd->scsi_cmd; + + if (ipr_cmd->dma_use_sg) { + if (scsi_cmd->use_sg > 0) { + pci_unmap_sg(ioa_cfg->pdev, scsi_cmd->request_buffer, + scsi_cmd->use_sg, + scsi_cmd->sc_data_direction); + } else { + pci_unmap_single(ioa_cfg->pdev, ipr_cmd->dma_handle, + scsi_cmd->request_bufflen, + scsi_cmd->sc_data_direction); + } + } +} + +/** + * ipr_mask_and_clear_interrupts - Mask all and clear specified interrupts + * @ioa_cfg: ioa config struct + * @clr_ints: interrupts to clear + * + * This function masks all interrupts on the adapter, then clears the + * interrupts specified in the mask + * + * Return value: + * none + **/ +static void ipr_mask_and_clear_interrupts(struct ipr_ioa_cfg *ioa_cfg, + u32 clr_ints) +{ + volatile u32 int_reg; + + /* Stop new interrupts */ + ioa_cfg->allow_interrupts = 0; + + /* Set interrupt mask to stop all new interrupts */ + writel(~0, ioa_cfg->regs.set_interrupt_mask_reg); + + /* Clear any pending interrupts */ + writel(clr_ints, ioa_cfg->regs.clr_interrupt_reg); + int_reg = readl(ioa_cfg->regs.sense_interrupt_reg); +} + +/** + * ipr_save_pcix_cmd_reg - Save PCI-X command register + * @ioa_cfg: ioa config struct + * + * Return value: + * 0 on success / -EIO on failure + **/ +static int ipr_save_pcix_cmd_reg(struct ipr_ioa_cfg *ioa_cfg) +{ + int pcix_cmd_reg = pci_find_capability(ioa_cfg->pdev, PCI_CAP_ID_PCIX); + + if (pcix_cmd_reg == 0) { + dev_err(&ioa_cfg->pdev->dev, "Failed to save PCI-X command register\n"); + return -EIO; + } + + if (pci_read_config_word(ioa_cfg->pdev, pcix_cmd_reg, + &ioa_cfg->saved_pcix_cmd_reg) != PCIBIOS_SUCCESSFUL) { + dev_err(&ioa_cfg->pdev->dev, "Failed to save PCI-X command register\n"); + return -EIO; + } + + ioa_cfg->saved_pcix_cmd_reg |= PCI_X_CMD_DPERR_E | PCI_X_CMD_ERO; + return 0; +} + +/** + * ipr_set_pcix_cmd_reg - Setup PCI-X command register + * @ioa_cfg: ioa config struct + * + * Return value: + * 0 on success / -EIO on failure + **/ +static int ipr_set_pcix_cmd_reg(struct ipr_ioa_cfg *ioa_cfg) +{ + int pcix_cmd_reg = pci_find_capability(ioa_cfg->pdev, PCI_CAP_ID_PCIX); + + if (pcix_cmd_reg) { + if (pci_write_config_word(ioa_cfg->pdev, pcix_cmd_reg, + ioa_cfg->saved_pcix_cmd_reg) != PCIBIOS_SUCCESSFUL) { + dev_err(&ioa_cfg->pdev->dev, "Failed to setup PCI-X command register\n"); + return -EIO; + } + } else { + dev_err(&ioa_cfg->pdev->dev, + "Failed to setup PCI-X command register\n"); + return -EIO; + } + + return 0; +} + +/** + * ipr_scsi_eh_done - mid-layer done function for aborted ops + * @ipr_cmd: ipr command struct + * + * This function is invoked by the interrupt handler for + * ops generated by the SCSI mid-layer which are being aborted. + * + * Return value: + * none + **/ +static void ipr_scsi_eh_done(struct ipr_cmnd *ipr_cmd) +{ + struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg; + struct scsi_cmnd *scsi_cmd = ipr_cmd->scsi_cmd; + + scsi_cmd->result |= (DID_ERROR << 16); + + ipr_unmap_sglist(ioa_cfg, ipr_cmd); + scsi_cmd->scsi_done(scsi_cmd); + list_add_tail(&ipr_cmd->queue, &ioa_cfg->free_q); +} + +/** + * ipr_fail_all_ops - Fails all outstanding ops. + * @ioa_cfg: ioa config struct + * + * This function fails all outstanding ops. + * + * Return value: + * none + **/ +static void ipr_fail_all_ops(struct ipr_ioa_cfg *ioa_cfg) +{ + struct ipr_cmnd *ipr_cmd, *temp; + + ENTER; + list_for_each_entry_safe(ipr_cmd, temp, &ioa_cfg->pending_q, queue) { + list_del(&ipr_cmd->queue); + + ipr_cmd->ioasa.ioasc = cpu_to_be32(IPR_IOASC_IOA_WAS_RESET); + ipr_cmd->ioasa.ilid = cpu_to_be32(IPR_DRIVER_ILID); + + if (ipr_cmd->scsi_cmd) + ipr_cmd->done = ipr_scsi_eh_done; + + ipr_trc_hook(ipr_cmd, IPR_TRACE_FINISH, IPR_IOASC_IOA_WAS_RESET); + del_timer(&ipr_cmd->timer); + ipr_cmd->done(ipr_cmd); + } + + LEAVE; +} + +/** + * ipr_do_req - Send driver initiated requests. + * @ipr_cmd: ipr command struct + * @done: done function + * @timeout_func: timeout function + * @timeout: timeout value + * + * This function sends the specified command to the adapter with the + * timeout given. The done function is invoked on command completion. + * + * Return value: + * none + **/ +static void ipr_do_req(struct ipr_cmnd *ipr_cmd, + void (*done) (struct ipr_cmnd *), + void (*timeout_func) (struct ipr_cmnd *), u32 timeout) +{ + struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg; + + list_add_tail(&ipr_cmd->queue, &ioa_cfg->pending_q); + + ipr_cmd->done = done; + + ipr_cmd->timer.data = (unsigned long) ipr_cmd; + ipr_cmd->timer.expires = jiffies + timeout; + ipr_cmd->timer.function = (void (*)(unsigned long))timeout_func; + + add_timer(&ipr_cmd->timer); + + ipr_trc_hook(ipr_cmd, IPR_TRACE_START, 0); + + mb(); + writel(be32_to_cpu(ipr_cmd->ioarcb.ioarcb_host_pci_addr), + ioa_cfg->regs.ioarrin_reg); +} + +/** + * ipr_internal_cmd_done - Op done function for an internally generated op. + * @ipr_cmd: ipr command struct + * + * This function is the op done function for an internally generated, + * blocking op. It simply wakes the sleeping thread. + * + * Return value: + * none + **/ +static void ipr_internal_cmd_done(struct ipr_cmnd *ipr_cmd) +{ + if (ipr_cmd->u.sibling) + ipr_cmd->u.sibling = NULL; + else + complete(&ipr_cmd->completion); +} + +/** + * ipr_send_blocking_cmd - Send command and sleep on its completion. + * @ipr_cmd: ipr command struct + * @timeout_func: function to invoke if command times out + * @timeout: timeout + * + * Return value: + * none + **/ +static void ipr_send_blocking_cmd(struct ipr_cmnd *ipr_cmd, + void (*timeout_func) (struct ipr_cmnd *ipr_cmd), + u32 timeout) +{ + struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg; + + init_completion(&ipr_cmd->completion); + ipr_do_req(ipr_cmd, ipr_internal_cmd_done, timeout_func, timeout); + + spin_unlock_irq(ioa_cfg->host->host_lock); + wait_for_completion(&ipr_cmd->completion); + spin_lock_irq(ioa_cfg->host->host_lock); +} + +/** + * ipr_send_hcam - Send an HCAM to the adapter. + * @ioa_cfg: ioa config struct + * @type: HCAM type + * @hostrcb: hostrcb struct + * + * This function will send a Host Controlled Async command to the adapter. + * If HCAMs are currently not allowed to be issued to the adapter, it will + * place the hostrcb on the free queue. + * + * Return value: + * none + **/ +static void ipr_send_hcam(struct ipr_ioa_cfg *ioa_cfg, u8 type, + struct ipr_hostrcb *hostrcb) +{ + struct ipr_cmnd *ipr_cmd; + struct ipr_ioarcb *ioarcb; + + if (ioa_cfg->allow_cmds) { + ipr_cmd = ipr_get_free_ipr_cmnd(ioa_cfg); + list_add_tail(&ipr_cmd->queue, &ioa_cfg->pending_q); + list_add_tail(&hostrcb->queue, &ioa_cfg->hostrcb_pending_q); + + ipr_cmd->u.hostrcb = hostrcb; + ioarcb = &ipr_cmd->ioarcb; + + ioarcb->res_handle = cpu_to_be32(IPR_IOA_RES_HANDLE); + ioarcb->cmd_pkt.request_type = IPR_RQTYPE_HCAM; + ioarcb->cmd_pkt.cdb[0] = IPR_HOST_CONTROLLED_ASYNC; + ioarcb->cmd_pkt.cdb[1] = type; + ioarcb->cmd_pkt.cdb[7] = (sizeof(hostrcb->hcam) >> 8) & 0xff; + ioarcb->cmd_pkt.cdb[8] = sizeof(hostrcb->hcam) & 0xff; + + ioarcb->read_data_transfer_length = cpu_to_be32(sizeof(hostrcb->hcam)); + ioarcb->read_ioadl_len = cpu_to_be32(sizeof(struct ipr_ioadl_desc)); + ipr_cmd->ioadl[0].flags_and_data_len = + cpu_to_be32(IPR_IOADL_FLAGS_READ_LAST | sizeof(hostrcb->hcam)); + ipr_cmd->ioadl[0].address = cpu_to_be32(hostrcb->hostrcb_dma); + + if (type == IPR_HCAM_CDB_OP_CODE_CONFIG_CHANGE) + ipr_cmd->done = ipr_process_ccn; + else + ipr_cmd->done = ipr_process_error; + + ipr_trc_hook(ipr_cmd, IPR_TRACE_START, IPR_IOA_RES_ADDR); + + mb(); + writel(be32_to_cpu(ipr_cmd->ioarcb.ioarcb_host_pci_addr), + ioa_cfg->regs.ioarrin_reg); + } else { + list_add_tail(&hostrcb->queue, &ioa_cfg->hostrcb_free_q); + } +} + +/** + * ipr_init_res_entry - Initialize a resource entry struct. + * @res: resource entry struct + * + * Return value: + * none + **/ +static void ipr_init_res_entry(struct ipr_resource_entry *res) +{ + res->needs_sync_complete = 1; + res->in_erp = 0; + res->add_to_ml = 0; + res->del_from_ml = 0; + res->resetting_device = 0; + res->tcq_active = 0; + res->qdepth = IPR_MAX_CMD_PER_LUN; + res->sdev = NULL; +} + +/** + * ipr_handle_config_change - Handle a config change from the adapter + * @ioa_cfg: ioa config struct + * @hostrcb: hostrcb + * + * Return value: + * none + **/ +static void ipr_handle_config_change(struct ipr_ioa_cfg *ioa_cfg, + struct ipr_hostrcb *hostrcb) +{ + struct ipr_resource_entry *res = NULL; + struct ipr_config_table_entry *cfgte; + u32 is_ndn = 1; + + cfgte = &hostrcb->hcam.u.ccn.cfgte; + + list_for_each_entry(res, &ioa_cfg->used_res_q, queue) { + if (!memcmp(&res->cfgte.res_addr, &cfgte->res_addr, + sizeof(cfgte->res_addr))) { + is_ndn = 0; + break; + } + } + + if (is_ndn) { + if (list_empty(&ioa_cfg->free_res_q)) { + ipr_send_hcam(ioa_cfg, + IPR_HCAM_CDB_OP_CODE_CONFIG_CHANGE, + hostrcb); + return; + } + + res = list_entry(ioa_cfg->free_res_q.next, + struct ipr_resource_entry, queue); + + list_del(&res->queue); + ipr_init_res_entry(res); + list_add_tail(&res->queue, &ioa_cfg->used_res_q); + } + + memcpy(&res->cfgte, cfgte, sizeof(struct ipr_config_table_entry)); + + if (hostrcb->hcam.notify_type == IPR_HOST_RCB_NOTIF_TYPE_REM_ENTRY) { + if (res->sdev) { + res->sdev->hostdata = NULL; + res->del_from_ml = 1; + if (ioa_cfg->allow_ml_add_del) + schedule_work(&ioa_cfg->work_q); + } else + list_move_tail(&res->queue, &ioa_cfg->free_res_q); + } else if (!res->sdev) { + res->add_to_ml = 1; + if (ioa_cfg->allow_ml_add_del) + schedule_work(&ioa_cfg->work_q); + } + + ipr_send_hcam(ioa_cfg, IPR_HCAM_CDB_OP_CODE_CONFIG_CHANGE, hostrcb); +} + +/** + * ipr_process_ccn - Op done function for a CCN. + * @ipr_cmd: ipr command struct + * + * This function is the op done function for a configuration + * change notification host controlled async from the adapter. + * + * Return value: + * none + **/ +static void ipr_process_ccn(struct ipr_cmnd *ipr_cmd) +{ + struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg; + struct ipr_hostrcb *hostrcb = ipr_cmd->u.hostrcb; + u32 ioasc = be32_to_cpu(ipr_cmd->ioasa.ioasc); + + list_del(&hostrcb->queue); + list_add_tail(&ipr_cmd->queue, &ioa_cfg->free_q); + + if (ioasc) { + if (ioasc != IPR_IOASC_IOA_WAS_RESET) + dev_err(&ioa_cfg->pdev->dev, + "Host RCB failed with IOASC: 0x%08X\n", ioasc); + + ipr_send_hcam(ioa_cfg, IPR_HCAM_CDB_OP_CODE_CONFIG_CHANGE, hostrcb); + } else { + ipr_handle_config_change(ioa_cfg, hostrcb); + } +} + +/** + * ipr_log_vpd - Log the passed VPD to the error log. + * @vpids: vendor/product id struct + * @serial_num: serial number string + * + * Return value: + * none + **/ +static void ipr_log_vpd(struct ipr_std_inq_vpids *vpids, u8 *serial_num) +{ + char buffer[max_t(int, sizeof(struct ipr_std_inq_vpids), + IPR_SERIAL_NUM_LEN) + 1]; + + memcpy(buffer, vpids, sizeof(struct ipr_std_inq_vpids)); + buffer[sizeof(struct ipr_std_inq_vpids)] = '\0'; + ipr_err("Vendor/Product ID: %s\n", buffer); + + memcpy(buffer, serial_num, IPR_SERIAL_NUM_LEN); + buffer[IPR_SERIAL_NUM_LEN] = '\0'; + ipr_err(" Serial Number: %s\n", buffer); +} + +/** + * ipr_log_cache_error - Log a cache error. + * @ioa_cfg: ioa config struct + * @hostrcb: hostrcb struct + * + * Return value: + * none + **/ +static void ipr_log_cache_error(struct ipr_ioa_cfg *ioa_cfg, + struct ipr_hostrcb *hostrcb) +{ + struct ipr_hostrcb_type_02_error *error = + &hostrcb->hcam.u.error.u.type_02_error; + + ipr_err("-----Current Configuration-----\n"); + ipr_err("Cache Directory Card Information:\n"); + ipr_log_vpd(&error->ioa_vpids, error->ioa_sn); + ipr_err("Adapter Card Information:\n"); + ipr_log_vpd(&error->cfc_vpids, error->cfc_sn); + + ipr_err("-----Expected Configuration-----\n"); + ipr_err("Cache Directory Card Information:\n"); + ipr_log_vpd(&error->ioa_last_attached_to_cfc_vpids, + error->ioa_last_attached_to_cfc_sn); + ipr_err("Adapter Card Information:\n"); + ipr_log_vpd(&error->cfc_last_attached_to_ioa_vpids, + error->cfc_last_attached_to_ioa_sn); + + ipr_err("Additional IOA Data: %08X %08X %08X\n", + be32_to_cpu(error->ioa_data[0]), + be32_to_cpu(error->ioa_data[1]), + be32_to_cpu(error->ioa_data[2])); +} + +/** + * ipr_log_config_error - Log a configuration error. + * @ioa_cfg: ioa config struct + * @hostrcb: hostrcb struct + * + * Return value: + * none + **/ +static void ipr_log_config_error(struct ipr_ioa_cfg *ioa_cfg, + struct ipr_hostrcb *hostrcb) +{ + int errors_logged, i; + struct ipr_hostrcb_device_data_entry *dev_entry; + struct ipr_hostrcb_type_03_error *error; + + error = &hostrcb->hcam.u.error.u.type_03_error; + errors_logged = be32_to_cpu(error->errors_logged); + + ipr_err("Device Errors Detected/Logged: %d/%d\n", + be32_to_cpu(error->errors_detected), errors_logged); + + dev_entry = error->dev_entry; + + for (i = 0; i < errors_logged; i++, dev_entry++) { + ipr_err_separator; + + if (dev_entry->dev_res_addr.bus >= IPR_MAX_NUM_BUSES) { + ipr_err("Device %d: missing\n", i + 1); + } else { + ipr_err("Device %d: %d:%d:%d:%d\n", i + 1, + ioa_cfg->host->host_no, dev_entry->dev_res_addr.bus, + dev_entry->dev_res_addr.target, dev_entry->dev_res_addr.lun); + } + ipr_log_vpd(&dev_entry->dev_vpids, dev_entry->dev_sn); + + ipr_err("-----New Device Information-----\n"); + ipr_log_vpd(&dev_entry->new_dev_vpids, dev_entry->new_dev_sn); + + ipr_err("Cache Directory Card Information:\n"); + ipr_log_vpd(&dev_entry->ioa_last_with_dev_vpids, + dev_entry->ioa_last_with_dev_sn); + + ipr_err("Adapter Card Information:\n"); + ipr_log_vpd(&dev_entry->cfc_last_with_dev_vpids, + dev_entry->cfc_last_with_dev_sn); + + ipr_err("Additional IOA Data: %08X %08X %08X %08X %08X\n", + be32_to_cpu(dev_entry->ioa_data[0]), + be32_to_cpu(dev_entry->ioa_data[1]), + be32_to_cpu(dev_entry->ioa_data[2]), + be32_to_cpu(dev_entry->ioa_data[3]), + be32_to_cpu(dev_entry->ioa_data[4])); + } +} + +/** + * ipr_log_array_error - Log an array configuration error. + * @ioa_cfg: ioa config struct + * @hostrcb: hostrcb struct + * + * Return value: + * none + **/ +static void ipr_log_array_error(struct ipr_ioa_cfg *ioa_cfg, + struct ipr_hostrcb *hostrcb) +{ + int i; + struct ipr_hostrcb_type_04_error *error; + struct ipr_hostrcb_array_data_entry *array_entry; + u8 zero_sn[IPR_SERIAL_NUM_LEN]; + + memset(zero_sn, '0', IPR_SERIAL_NUM_LEN); + + error = &hostrcb->hcam.u.error.u.type_04_error; + + ipr_err_separator; + + ipr_err("RAID %s Array Configuration: %d:%d:%d:%d\n", + error->protection_level, + ioa_cfg->host->host_no, + error->last_func_vset_res_addr.bus, + error->last_func_vset_res_addr.target, + error->last_func_vset_res_addr.lun); + + ipr_err_separator; + + array_entry = error->array_member; + + for (i = 0; i < 18; i++) { + if (!memcmp(array_entry->serial_num, zero_sn, IPR_SERIAL_NUM_LEN)) + continue; + + if (error->exposed_mode_adn == i) { + ipr_err("Exposed Array Member %d:\n", i); + } else { + ipr_err("Array Member %d:\n", i); + } + + ipr_log_vpd(&array_entry->vpids, array_entry->serial_num); + + if (array_entry->dev_res_addr.bus >= IPR_MAX_NUM_BUSES) { + ipr_err("Current Location: unknown\n"); + } else { + ipr_err("Current Location: %d:%d:%d:%d\n", + ioa_cfg->host->host_no, + array_entry->dev_res_addr.bus, + array_entry->dev_res_addr.target, + array_entry->dev_res_addr.lun); + } + + if (array_entry->dev_res_addr.bus >= IPR_MAX_NUM_BUSES) { + ipr_err("Expected Location: unknown\n"); + } else { + ipr_err("Expected Location: %d:%d:%d:%d\n", + ioa_cfg->host->host_no, + array_entry->expected_dev_res_addr.bus, + array_entry->expected_dev_res_addr.target, + array_entry->expected_dev_res_addr.lun); + } + + ipr_err_separator; + + if (i == 9) + array_entry = error->array_member2; + else + array_entry++; + } +} + +/** + * ipr_log_generic_error - Log an adapter error. + * @ioa_cfg: ioa config struct + * @hostrcb: hostrcb struct + * + * Return value: + * none + **/ +static void ipr_log_generic_error(struct ipr_ioa_cfg *ioa_cfg, + struct ipr_hostrcb *hostrcb) +{ + int i; + int ioa_data_len = be32_to_cpu(hostrcb->hcam.length); + + if (ioa_data_len == 0) + return; + + ipr_err("IOA Error Data:\n"); + ipr_err("Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F\n"); + + for (i = 0; i < ioa_data_len / 4; i += 4) { + ipr_err("%08X: %08X %08X %08X %08X\n", i*4, + be32_to_cpu(hostrcb->hcam.u.raw.data[i]), + be32_to_cpu(hostrcb->hcam.u.raw.data[i+1]), + be32_to_cpu(hostrcb->hcam.u.raw.data[i+2]), + be32_to_cpu(hostrcb->hcam.u.raw.data[i+3])); + } +} + +/** + * ipr_get_error - Find the specfied IOASC in the ipr_error_table. + * @ioasc: IOASC + * + * This function will return the index of into the ipr_error_table + * for the specified IOASC. If the IOASC is not in the table, + * 0 will be returned, which points to the entry used for unknown errors. + * + * Return value: + * index into the ipr_error_table + **/ +static u32 ipr_get_error(u32 ioasc) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ipr_error_table); i++) + if (ipr_error_table[i].ioasc == ioasc) + return i; + + return 0; +} + +/** + * ipr_handle_log_data - Log an adapter error. + * @ioa_cfg: ioa config struct + * @hostrcb: hostrcb struct + * + * This function logs an adapter error to the system. + * + * Return value: + * none + **/ +static void ipr_handle_log_data(struct ipr_ioa_cfg *ioa_cfg, + struct ipr_hostrcb *hostrcb) +{ + u32 ioasc; + int error_index; + + if (hostrcb->hcam.notify_type != IPR_HOST_RCB_NOTIF_TYPE_ERROR_LOG_ENTRY) + return; + + if (hostrcb->hcam.notifications_lost == IPR_HOST_RCB_NOTIFICATIONS_LOST) + dev_err(&ioa_cfg->pdev->dev, "Error notifications lost\n"); + + ioasc = be32_to_cpu(hostrcb->hcam.u.error.failing_dev_ioasc); + + if (ioasc == IPR_IOASC_BUS_WAS_RESET || + ioasc == IPR_IOASC_BUS_WAS_RESET_BY_OTHER) { + /* Tell the midlayer we had a bus reset so it will handle the UA properly */ + scsi_report_bus_reset(ioa_cfg->host, + hostrcb->hcam.u.error.failing_dev_res_addr.bus); + } + + error_index = ipr_get_error(ioasc); + + if (!ipr_error_table[error_index].log_hcam) + return; + + if (ipr_is_device(&hostrcb->hcam.u.error.failing_dev_res_addr)) { + ipr_res_err(ioa_cfg, hostrcb->hcam.u.error.failing_dev_res_addr, + "%s\n", ipr_error_table[error_index].error); + } else { + dev_err(&ioa_cfg->pdev->dev, "%s\n", + ipr_error_table[error_index].error); + } + + /* Set indication we have logged an error */ + ioa_cfg->errors_logged++; + + if (ioa_cfg->log_level < IPR_DEFAULT_LOG_LEVEL) + return; + + switch (hostrcb->hcam.overlay_id) { + case IPR_HOST_RCB_OVERLAY_ID_1: + ipr_log_generic_error(ioa_cfg, hostrcb); + break; + case IPR_HOST_RCB_OVERLAY_ID_2: + ipr_log_cache_error(ioa_cfg, hostrcb); + break; + case IPR_HOST_RCB_OVERLAY_ID_3: + ipr_log_config_error(ioa_cfg, hostrcb); + break; + case IPR_HOST_RCB_OVERLAY_ID_4: + case IPR_HOST_RCB_OVERLAY_ID_6: + ipr_log_array_error(ioa_cfg, hostrcb); + break; + case IPR_HOST_RCB_OVERLAY_ID_DEFAULT: + ipr_log_generic_error(ioa_cfg, hostrcb); + break; + default: + dev_err(&ioa_cfg->pdev->dev, + "Unknown error received. Overlay ID: %d\n", + hostrcb->hcam.overlay_id); + break; + } +} + +/** + * ipr_process_error - Op done function for an adapter error log. + * @ipr_cmd: ipr command struct + * + * This function is the op done function for an error log host + * controlled async from the adapter. It will log the error and + * send the HCAM back to the adapter. + * + * Return value: + * none + **/ +static void ipr_process_error(struct ipr_cmnd *ipr_cmd) +{ + struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg; + struct ipr_hostrcb *hostrcb = ipr_cmd->u.hostrcb; + u32 ioasc = be32_to_cpu(ipr_cmd->ioasa.ioasc); + + list_del(&hostrcb->queue); + list_add_tail(&ipr_cmd->queue, &ioa_cfg->free_q); + + if (!ioasc) { + ipr_handle_log_data(ioa_cfg, hostrcb); + } else if (ioasc != IPR_IOASC_IOA_WAS_RESET) { + dev_err(&ioa_cfg->pdev->dev, + "Host RCB failed with IOASC: 0x%08X\n", ioasc); + } + + ipr_send_hcam(ioa_cfg, IPR_HCAM_CDB_OP_CODE_LOG_DATA, hostrcb); +} + +/** + * ipr_timeout - An internally generated op has timed out. + * @ipr_cmd: ipr command struct + * + * This function blocks host requests and initiates an + * adapter reset. + * + * Return value: + * none + **/ +static void ipr_timeout(struct ipr_cmnd *ipr_cmd) +{ + unsigned long lock_flags = 0; + struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg; + + ENTER; + spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); + + ioa_cfg->errors_logged++; + dev_err(&ioa_cfg->pdev->dev, + "Adapter being reset due to command timeout.\n"); + + if (WAIT_FOR_DUMP == ioa_cfg->sdt_state) + ioa_cfg->sdt_state = GET_DUMP; + + if (!ioa_cfg->in_reset_reload || ioa_cfg->reset_cmd == ipr_cmd) + ipr_initiate_ioa_reset(ioa_cfg, IPR_SHUTDOWN_NONE); + + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + LEAVE; +} + +/** + * ipr_reset_reload - Reset/Reload the IOA + * @ioa_cfg: ioa config struct + * @shutdown_type: shutdown type + * + * This function resets the adapter and re-initializes it. + * This function assumes that all new host commands have been stopped. + * Return value: + * SUCCESS / FAILED + **/ +static int ipr_reset_reload(struct ipr_ioa_cfg *ioa_cfg, + enum ipr_shutdown_type shutdown_type) +{ + if (!ioa_cfg->in_reset_reload) + ipr_initiate_ioa_reset(ioa_cfg, shutdown_type); + + spin_unlock_irq(ioa_cfg->host->host_lock); + wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload); + spin_lock_irq(ioa_cfg->host->host_lock); + + /* If we got hit with a host reset while we were already resetting + the adapter for some reason, and the reset failed. */ + if (ioa_cfg->ioa_is_dead) { + ipr_trace; + return FAILED; + } + + return SUCCESS; +} + +/** + * ipr_find_ses_entry - Find matching SES in SES table + * @res: resource entry struct of SES + * + * Return value: + * pointer to SES table entry / NULL on failure + **/ +static const struct ipr_ses_table_entry * +ipr_find_ses_entry(struct ipr_resource_entry *res) +{ + int i, j, matches; + const struct ipr_ses_table_entry *ste = ipr_ses_table; + + for (i = 0; i < ARRAY_SIZE(ipr_ses_table); i++, ste++) { + for (j = 0, matches = 0; j < IPR_PROD_ID_LEN; j++) { + if (ste->compare_product_id_byte[j] == 'X') { + if (res->cfgte.std_inq_data.vpids.product_id[j] == ste->product_id[j]) + matches++; + else + break; + } else + matches++; + } + + if (matches == IPR_PROD_ID_LEN) + return ste; + } + + return NULL; +} + +/** + * ipr_get_max_scsi_speed - Determine max SCSI speed for a given bus + * @ioa_cfg: ioa config struct + * @bus: SCSI bus + * @bus_width: bus width + * + * Return value: + * SCSI bus speed in units of 100KHz, 1600 is 160 MHz + * For a 2-byte wide SCSI bus, the maximum transfer speed is + * twice the maximum transfer rate (e.g. for a wide enabled bus, + * max 160MHz = max 320MB/sec). + **/ +static u32 ipr_get_max_scsi_speed(struct ipr_ioa_cfg *ioa_cfg, u8 bus, u8 bus_width) +{ + struct ipr_resource_entry *res; + const struct ipr_ses_table_entry *ste; + u32 max_xfer_rate = IPR_MAX_SCSI_RATE(bus_width); + + /* Loop through each config table entry in the config table buffer */ + list_for_each_entry(res, &ioa_cfg->used_res_q, queue) { + if (!(IPR_IS_SES_DEVICE(res->cfgte.std_inq_data))) + continue; + + if (bus != res->cfgte.res_addr.bus) + continue; + + if (!(ste = ipr_find_ses_entry(res))) + continue; + + max_xfer_rate = (ste->max_bus_speed_limit * 10) / (bus_width / 8); + } + + return max_xfer_rate; +} + +/** + * ipr_wait_iodbg_ack - Wait for an IODEBUG ACK from the IOA + * @ioa_cfg: ioa config struct + * @max_delay: max delay in micro-seconds to wait + * + * Waits for an IODEBUG ACK from the IOA, doing busy looping. + * + * Return value: + * 0 on success / other on failure + **/ +static int ipr_wait_iodbg_ack(struct ipr_ioa_cfg *ioa_cfg, int max_delay) +{ + volatile u32 pcii_reg; + int delay = 1; + + /* Read interrupt reg until IOA signals IO Debug Acknowledge */ + while (delay < max_delay) { + pcii_reg = readl(ioa_cfg->regs.sense_interrupt_reg); + + if (pcii_reg & IPR_PCII_IO_DEBUG_ACKNOWLEDGE) + return 0; + + /* udelay cannot be used if delay is more than a few milliseconds */ + if ((delay / 1000) > MAX_UDELAY_MS) + mdelay(delay / 1000); + else + udelay(delay); + + delay += delay; + } + return -EIO; +} + +/** + * ipr_get_ldump_data_section - Dump IOA memory + * @ioa_cfg: ioa config struct + * @start_addr: adapter address to dump + * @dest: destination kernel buffer + * @length_in_words: length to dump in 4 byte words + * + * Return value: + * 0 on success / -EIO on failure + **/ +static int ipr_get_ldump_data_section(struct ipr_ioa_cfg *ioa_cfg, + u32 start_addr, + u32 *dest, u32 length_in_words) +{ + volatile u32 temp_pcii_reg; + int i, delay = 0; + + /* Write IOA interrupt reg starting LDUMP state */ + writel((IPR_UPROCI_RESET_ALERT | IPR_UPROCI_IO_DEBUG_ALERT), + ioa_cfg->regs.set_uproc_interrupt_reg); + + /* Wait for IO debug acknowledge */ + if (ipr_wait_iodbg_ack(ioa_cfg, + IPR_LDUMP_MAX_LONG_ACK_DELAY_IN_USEC)) { + dev_err(&ioa_cfg->pdev->dev, + "IOA dump long data transfer timeout\n"); + return -EIO; + } + + /* Signal LDUMP interlocked - clear IO debug ack */ + writel(IPR_PCII_IO_DEBUG_ACKNOWLEDGE, + ioa_cfg->regs.clr_interrupt_reg); + + /* Write Mailbox with starting address */ + writel(start_addr, ioa_cfg->ioa_mailbox); + + /* Signal address valid - clear IOA Reset alert */ + writel(IPR_UPROCI_RESET_ALERT, + ioa_cfg->regs.clr_uproc_interrupt_reg); + + for (i = 0; i < length_in_words; i++) { + /* Wait for IO debug acknowledge */ + if (ipr_wait_iodbg_ack(ioa_cfg, + IPR_LDUMP_MAX_SHORT_ACK_DELAY_IN_USEC)) { + dev_err(&ioa_cfg->pdev->dev, + "IOA dump short data transfer timeout\n"); + return -EIO; + } + + /* Read data from mailbox and increment destination pointer */ + *dest = cpu_to_be32(readl(ioa_cfg->ioa_mailbox)); + dest++; + + /* For all but the last word of data, signal data received */ + if (i < (length_in_words - 1)) { + /* Signal dump data received - Clear IO debug Ack */ + writel(IPR_PCII_IO_DEBUG_ACKNOWLEDGE, + ioa_cfg->regs.clr_interrupt_reg); + } + } + + /* Signal end of block transfer. Set reset alert then clear IO debug ack */ + writel(IPR_UPROCI_RESET_ALERT, + ioa_cfg->regs.set_uproc_interrupt_reg); + + writel(IPR_UPROCI_IO_DEBUG_ALERT, + ioa_cfg->regs.clr_uproc_interrupt_reg); + + /* Signal dump data received - Clear IO debug Ack */ + writel(IPR_PCII_IO_DEBUG_ACKNOWLEDGE, + ioa_cfg->regs.clr_interrupt_reg); + + /* Wait for IOA to signal LDUMP exit - IOA reset alert will be cleared */ + while (delay < IPR_LDUMP_MAX_SHORT_ACK_DELAY_IN_USEC) { + temp_pcii_reg = + readl(ioa_cfg->regs.sense_uproc_interrupt_reg); + + if (!(temp_pcii_reg & IPR_UPROCI_RESET_ALERT)) + return 0; + + udelay(10); + delay += 10; + } + + return 0; +} + +#ifdef CONFIG_SCSI_IPR_DUMP +/** + * ipr_sdt_copy - Copy Smart Dump Table to kernel buffer + * @ioa_cfg: ioa config struct + * @pci_address: adapter address + * @length: length of data to copy + * + * Copy data from PCI adapter to kernel buffer. + * Note: length MUST be a 4 byte multiple + * Return value: + * 0 on success / other on failure + **/ +static int ipr_sdt_copy(struct ipr_ioa_cfg *ioa_cfg, + unsigned long pci_address, u32 length) +{ + int bytes_copied = 0; + int cur_len, rc, rem_len, rem_page_len; + u32 *page; + unsigned long lock_flags = 0; + struct ipr_ioa_dump *ioa_dump = &ioa_cfg->dump->ioa_dump; + + while (bytes_copied < length && + (ioa_dump->hdr.len + bytes_copied) < IPR_MAX_IOA_DUMP_SIZE) { + if (ioa_dump->page_offset >= PAGE_SIZE || + ioa_dump->page_offset == 0) { + page = (u32 *)__get_free_page(GFP_ATOMIC); + + if (!page) { + ipr_trace; + return bytes_copied; + } + + ioa_dump->page_offset = 0; + ioa_dump->ioa_data[ioa_dump->next_page_index] = page; + ioa_dump->next_page_index++; + } else + page = ioa_dump->ioa_data[ioa_dump->next_page_index - 1]; + + rem_len = length - bytes_copied; + rem_page_len = PAGE_SIZE - ioa_dump->page_offset; + cur_len = min(rem_len, rem_page_len); + + spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); + if (ioa_cfg->sdt_state == ABORT_DUMP) { + rc = -EIO; + } else { + rc = ipr_get_ldump_data_section(ioa_cfg, + pci_address + bytes_copied, + &page[ioa_dump->page_offset / 4], + (cur_len / sizeof(u32))); + } + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + + if (!rc) { + ioa_dump->page_offset += cur_len; + bytes_copied += cur_len; + } else { + ipr_trace; + break; + } + schedule(); + } + + return bytes_copied; +} + +/** + * ipr_init_dump_entry_hdr - Initialize a dump entry header. + * @hdr: dump entry header struct + * + * Return value: + * nothing + **/ +static void ipr_init_dump_entry_hdr(struct ipr_dump_entry_header *hdr) +{ + hdr->eye_catcher = IPR_DUMP_EYE_CATCHER; + hdr->num_elems = 1; + hdr->offset = sizeof(*hdr); + hdr->status = IPR_DUMP_STATUS_SUCCESS; +} + +/** + * ipr_dump_ioa_type_data - Fill in the adapter type in the dump. + * @ioa_cfg: ioa config struct + * @driver_dump: driver dump struct + * + * Return value: + * nothing + **/ +static void ipr_dump_ioa_type_data(struct ipr_ioa_cfg *ioa_cfg, + struct ipr_driver_dump *driver_dump) +{ + struct ipr_inquiry_page3 *ucode_vpd = &ioa_cfg->vpd_cbs->page3_data; + + ipr_init_dump_entry_hdr(&driver_dump->ioa_type_entry.hdr); + driver_dump->ioa_type_entry.hdr.len = + sizeof(struct ipr_dump_ioa_type_entry) - + sizeof(struct ipr_dump_entry_header); + driver_dump->ioa_type_entry.hdr.data_type = IPR_DUMP_DATA_TYPE_BINARY; + driver_dump->ioa_type_entry.hdr.id = IPR_DUMP_DRIVER_TYPE_ID; + driver_dump->ioa_type_entry.type = ioa_cfg->type; + driver_dump->ioa_type_entry.fw_version = (ucode_vpd->major_release << 24) | + (ucode_vpd->card_type << 16) | (ucode_vpd->minor_release[0] << 8) | + ucode_vpd->minor_release[1]; + driver_dump->hdr.num_entries++; +} + +/** + * ipr_dump_version_data - Fill in the driver version in the dump. + * @ioa_cfg: ioa config struct + * @driver_dump: driver dump struct + * + * Return value: + * nothing + **/ +static void ipr_dump_version_data(struct ipr_ioa_cfg *ioa_cfg, + struct ipr_driver_dump *driver_dump) +{ + ipr_init_dump_entry_hdr(&driver_dump->version_entry.hdr); + driver_dump->version_entry.hdr.len = + sizeof(struct ipr_dump_version_entry) - + sizeof(struct ipr_dump_entry_header); + driver_dump->version_entry.hdr.data_type = IPR_DUMP_DATA_TYPE_ASCII; + driver_dump->version_entry.hdr.id = IPR_DUMP_DRIVER_VERSION_ID; + strcpy(driver_dump->version_entry.version, IPR_DRIVER_VERSION); + driver_dump->hdr.num_entries++; +} + +/** + * ipr_dump_trace_data - Fill in the IOA trace in the dump. + * @ioa_cfg: ioa config struct + * @driver_dump: driver dump struct + * + * Return value: + * nothing + **/ +static void ipr_dump_trace_data(struct ipr_ioa_cfg *ioa_cfg, + struct ipr_driver_dump *driver_dump) +{ + ipr_init_dump_entry_hdr(&driver_dump->trace_entry.hdr); + driver_dump->trace_entry.hdr.len = + sizeof(struct ipr_dump_trace_entry) - + sizeof(struct ipr_dump_entry_header); + driver_dump->trace_entry.hdr.data_type = IPR_DUMP_DATA_TYPE_BINARY; + driver_dump->trace_entry.hdr.id = IPR_DUMP_TRACE_ID; + memcpy(driver_dump->trace_entry.trace, ioa_cfg->trace, IPR_TRACE_SIZE); + driver_dump->hdr.num_entries++; +} + +/** + * ipr_dump_location_data - Fill in the IOA location in the dump. + * @ioa_cfg: ioa config struct + * @driver_dump: driver dump struct + * + * Return value: + * nothing + **/ +static void ipr_dump_location_data(struct ipr_ioa_cfg *ioa_cfg, + struct ipr_driver_dump *driver_dump) +{ + ipr_init_dump_entry_hdr(&driver_dump->location_entry.hdr); + driver_dump->location_entry.hdr.len = + sizeof(struct ipr_dump_location_entry) - + sizeof(struct ipr_dump_entry_header); + driver_dump->location_entry.hdr.data_type = IPR_DUMP_DATA_TYPE_ASCII; + driver_dump->location_entry.hdr.id = IPR_DUMP_LOCATION_ID; + strcpy(driver_dump->location_entry.location, ioa_cfg->pdev->dev.bus_id); + driver_dump->hdr.num_entries++; +} + +/** + * ipr_get_ioa_dump - Perform a dump of the driver and adapter. + * @ioa_cfg: ioa config struct + * @dump: dump struct + * + * Return value: + * nothing + **/ +static void ipr_get_ioa_dump(struct ipr_ioa_cfg *ioa_cfg, struct ipr_dump *dump) +{ + unsigned long start_addr, sdt_word; + unsigned long lock_flags = 0; + struct ipr_driver_dump *driver_dump = &dump->driver_dump; + struct ipr_ioa_dump *ioa_dump = &dump->ioa_dump; + u32 num_entries, start_off, end_off; + u32 bytes_to_copy, bytes_copied, rc; + struct ipr_sdt *sdt; + int i; + + ENTER; + + spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); + + if (ioa_cfg->sdt_state != GET_DUMP) { + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + return; + } + + start_addr = readl(ioa_cfg->ioa_mailbox); + + if (!ipr_sdt_is_fmt2(start_addr)) { + dev_err(&ioa_cfg->pdev->dev, + "Invalid dump table format: %lx\n", start_addr); + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + return; + } + + dev_err(&ioa_cfg->pdev->dev, "Dump of IOA initiated\n"); + + driver_dump->hdr.eye_catcher = IPR_DUMP_EYE_CATCHER; + + /* Initialize the overall dump header */ + driver_dump->hdr.len = sizeof(struct ipr_driver_dump); + driver_dump->hdr.num_entries = 1; + driver_dump->hdr.first_entry_offset = sizeof(struct ipr_dump_header); + driver_dump->hdr.status = IPR_DUMP_STATUS_SUCCESS; + driver_dump->hdr.os = IPR_DUMP_OS_LINUX; + driver_dump->hdr.driver_name = IPR_DUMP_DRIVER_NAME; + + ipr_dump_version_data(ioa_cfg, driver_dump); + ipr_dump_location_data(ioa_cfg, driver_dump); + ipr_dump_ioa_type_data(ioa_cfg, driver_dump); + ipr_dump_trace_data(ioa_cfg, driver_dump); + + /* Update dump_header */ + driver_dump->hdr.len += sizeof(struct ipr_dump_entry_header); + + /* IOA Dump entry */ + ipr_init_dump_entry_hdr(&ioa_dump->hdr); + ioa_dump->format = IPR_SDT_FMT2; + ioa_dump->hdr.len = 0; + ioa_dump->hdr.data_type = IPR_DUMP_DATA_TYPE_BINARY; + ioa_dump->hdr.id = IPR_DUMP_IOA_DUMP_ID; + + /* First entries in sdt are actually a list of dump addresses and + lengths to gather the real dump data. sdt represents the pointer + to the ioa generated dump table. Dump data will be extracted based + on entries in this table */ + sdt = &ioa_dump->sdt; + + rc = ipr_get_ldump_data_section(ioa_cfg, start_addr, (u32 *)sdt, + sizeof(struct ipr_sdt) / sizeof(u32)); + + /* Smart Dump table is ready to use and the first entry is valid */ + if (rc || (be32_to_cpu(sdt->hdr.state) != IPR_FMT2_SDT_READY_TO_USE)) { + dev_err(&ioa_cfg->pdev->dev, + "Dump of IOA failed. Dump table not valid: %d, %X.\n", + rc, be32_to_cpu(sdt->hdr.state)); + driver_dump->hdr.status = IPR_DUMP_STATUS_FAILED; + ioa_cfg->sdt_state = DUMP_OBTAINED; + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + return; + } + + num_entries = be32_to_cpu(sdt->hdr.num_entries_used); + + if (num_entries > IPR_NUM_SDT_ENTRIES) + num_entries = IPR_NUM_SDT_ENTRIES; + + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + + for (i = 0; i < num_entries; i++) { + if (ioa_dump->hdr.len > IPR_MAX_IOA_DUMP_SIZE) { + driver_dump->hdr.status = IPR_DUMP_STATUS_QUAL_SUCCESS; + break; + } + + if (sdt->entry[i].flags & IPR_SDT_VALID_ENTRY) { + sdt_word = be32_to_cpu(sdt->entry[i].bar_str_offset); + start_off = sdt_word & IPR_FMT2_MBX_ADDR_MASK; + end_off = be32_to_cpu(sdt->entry[i].end_offset); + + if (ipr_sdt_is_fmt2(sdt_word) && sdt_word) { + bytes_to_copy = end_off - start_off; + if (bytes_to_copy > IPR_MAX_IOA_DUMP_SIZE) { + sdt->entry[i].flags &= ~IPR_SDT_VALID_ENTRY; + continue; + } + + /* Copy data from adapter to driver buffers */ + bytes_copied = ipr_sdt_copy(ioa_cfg, sdt_word, + bytes_to_copy); + + ioa_dump->hdr.len += bytes_copied; + + if (bytes_copied != bytes_to_copy) { + driver_dump->hdr.status = IPR_DUMP_STATUS_QUAL_SUCCESS; + break; + } + } + } + } + + dev_err(&ioa_cfg->pdev->dev, "Dump of IOA completed.\n"); + + /* Update dump_header */ + driver_dump->hdr.len += ioa_dump->hdr.len; + wmb(); + ioa_cfg->sdt_state = DUMP_OBTAINED; + LEAVE; +} + +#else +#define ipr_get_ioa_dump(ioa_cfg, dump) do { } while(0) +#endif + +/** + * ipr_worker_thread - Worker thread + * @data: ioa config struct + * + * Called at task level from a work thread. This function takes care + * of adding and removing device from the mid-layer as configuration + * changes are detected by the adapter. + * + * Return value: + * nothing + **/ +static void ipr_worker_thread(void *data) +{ + unsigned long lock_flags; + struct ipr_resource_entry *res; + struct scsi_device *sdev; + struct ipr_dump *dump; + struct ipr_ioa_cfg *ioa_cfg = data; + u8 bus, target, lun; + int did_work; + + ENTER; + spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); + + if (ioa_cfg->sdt_state == GET_DUMP) { + dump = ioa_cfg->dump; + if (!dump || !kobject_get(&dump->kobj)) { + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + return; + } + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + ipr_get_ioa_dump(ioa_cfg, dump); + kobject_put(&dump->kobj); + + spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); + if (ioa_cfg->sdt_state == DUMP_OBTAINED) + ipr_initiate_ioa_reset(ioa_cfg, IPR_SHUTDOWN_NONE); + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + return; + } + +restart: + do { + did_work = 0; + if (!ioa_cfg->allow_cmds || !ioa_cfg->allow_ml_add_del) { + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + return; + } + + list_for_each_entry(res, &ioa_cfg->used_res_q, queue) { + if (res->del_from_ml && res->sdev) { + did_work = 1; + sdev = res->sdev; + if (!scsi_device_get(sdev)) { + res->sdev = NULL; + list_move_tail(&res->queue, &ioa_cfg->free_res_q); + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + scsi_remove_device(sdev); + scsi_device_put(sdev); + spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); + } + break; + } + } + } while(did_work); + + list_for_each_entry(res, &ioa_cfg->used_res_q, queue) { + if (res->add_to_ml) { + bus = res->cfgte.res_addr.bus; + target = res->cfgte.res_addr.target; + lun = res->cfgte.res_addr.lun; + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + scsi_add_device(ioa_cfg->host, bus, target, lun); + spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); + goto restart; + } + } + + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + LEAVE; +} + +#ifdef CONFIG_SCSI_IPR_TRACE +/** + * ipr_read_trace - Dump the adapter trace + * @kobj: kobject struct + * @buf: buffer + * @off: offset + * @count: buffer size + * + * Return value: + * number of bytes printed to buffer + **/ +static ssize_t ipr_read_trace(struct kobject *kobj, char *buf, + loff_t off, size_t count) +{ + struct class_device *cdev = container_of(kobj,struct class_device,kobj); + struct Scsi_Host *shost = class_to_shost(cdev); + struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)shost->hostdata; + unsigned long lock_flags = 0; + int size = IPR_TRACE_SIZE; + char *src = (char *)ioa_cfg->trace; + + if (off > size) + return 0; + if (off + count > size) { + size -= off; + count = size; + } + + spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); + memcpy(buf, &src[off], count); + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + return count; +} + +static struct bin_attribute ipr_trace_attr = { + .attr = { + .name = "trace", + .mode = S_IRUGO, + }, + .size = 0, + .read = ipr_read_trace, +}; +#endif + +/** + * ipr_show_fw_version - Show the firmware version + * @class_dev: class device struct + * @buf: buffer + * + * Return value: + * number of bytes printed to buffer + **/ +static ssize_t ipr_show_fw_version(struct class_device *class_dev, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(class_dev); + struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)shost->hostdata; + struct ipr_inquiry_page3 *ucode_vpd = &ioa_cfg->vpd_cbs->page3_data; + unsigned long lock_flags = 0; + int len; + + spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); + len = snprintf(buf, PAGE_SIZE, "%02X%02X%02X%02X\n", + ucode_vpd->major_release, ucode_vpd->card_type, + ucode_vpd->minor_release[0], + ucode_vpd->minor_release[1]); + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + return len; +} + +static struct class_device_attribute ipr_fw_version_attr = { + .attr = { + .name = "fw_version", + .mode = S_IRUGO, + }, + .show = ipr_show_fw_version, +}; + +/** + * ipr_show_log_level - Show the adapter's error logging level + * @class_dev: class device struct + * @buf: buffer + * + * Return value: + * number of bytes printed to buffer + **/ +static ssize_t ipr_show_log_level(struct class_device *class_dev, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(class_dev); + struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)shost->hostdata; + unsigned long lock_flags = 0; + int len; + + spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); + len = snprintf(buf, PAGE_SIZE, "%d\n", ioa_cfg->log_level); + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + return len; +} + +/** + * ipr_store_log_level - Change the adapter's error logging level + * @class_dev: class device struct + * @buf: buffer + * + * Return value: + * number of bytes printed to buffer + **/ +static ssize_t ipr_store_log_level(struct class_device *class_dev, + const char *buf, size_t count) +{ + struct Scsi_Host *shost = class_to_shost(class_dev); + struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)shost->hostdata; + unsigned long lock_flags = 0; + + spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); + ioa_cfg->log_level = simple_strtoul(buf, NULL, 10); + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + return strlen(buf); +} + +static struct class_device_attribute ipr_log_level_attr = { + .attr = { + .name = "log_level", + .mode = S_IRUGO | S_IWUSR, + }, + .show = ipr_show_log_level, + .store = ipr_store_log_level +}; + +/** + * ipr_store_diagnostics - IOA Diagnostics interface + * @class_dev: class_device struct + * @buf: buffer + * @count: buffer size + * + * This function will reset the adapter and wait a reasonable + * amount of time for any errors that the adapter might log. + * + * Return value: + * count on success / other on failure + **/ +static ssize_t ipr_store_diagnostics(struct class_device *class_dev, + const char *buf, size_t count) +{ + struct Scsi_Host *shost = class_to_shost(class_dev); + struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)shost->hostdata; + unsigned long lock_flags = 0; + int rc = count; + + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + + wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload); + spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); + ioa_cfg->errors_logged = 0; + ipr_initiate_ioa_reset(ioa_cfg, IPR_SHUTDOWN_NORMAL); + + if (ioa_cfg->in_reset_reload) { + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload); + + /* Wait for a second for any errors to be logged */ + schedule_timeout(HZ); + } else { + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + return -EIO; + } + + spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); + if (ioa_cfg->in_reset_reload || ioa_cfg->errors_logged) + rc = -EIO; + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + + return rc; +} + +static struct class_device_attribute ipr_diagnostics_attr = { + .attr = { + .name = "run_diagnostics", + .mode = S_IWUSR, + }, + .store = ipr_store_diagnostics +}; + +/** + * ipr_store_reset_adapter - Reset the adapter + * @class_dev: class_device struct + * @buf: buffer + * @count: buffer size + * + * This function will reset the adapter. + * + * Return value: + * count on success / other on failure + **/ +static ssize_t ipr_store_reset_adapter(struct class_device *class_dev, + const char *buf, size_t count) +{ + struct Scsi_Host *shost = class_to_shost(class_dev); + struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)shost->hostdata; + unsigned long lock_flags; + int result = count; + + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + + spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); + if (!ioa_cfg->in_reset_reload) + ipr_initiate_ioa_reset(ioa_cfg, IPR_SHUTDOWN_NORMAL); + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload); + + return result; +} + +static struct class_device_attribute ipr_ioa_reset_attr = { + .attr = { + .name = "reset_host", + .mode = S_IWUSR, + }, + .store = ipr_store_reset_adapter +}; + +/** + * ipr_alloc_ucode_buffer - Allocates a microcode download buffer + * @buf_len: buffer length + * + * Allocates a DMA'able buffer in chunks and assembles a scatter/gather + * list to use for microcode download + * + * Return value: + * pointer to sglist / NULL on failure + **/ +static struct ipr_sglist *ipr_alloc_ucode_buffer(int buf_len) +{ + int sg_size, order, bsize_elem, num_elem, i, j; + struct ipr_sglist *sglist; + struct scatterlist *scatterlist; + struct page *page; + + /* Get the minimum size per scatter/gather element */ + sg_size = buf_len / (IPR_MAX_SGLIST - 1); + + /* Get the actual size per element */ + order = get_order(sg_size); + + /* Determine the actual number of bytes per element */ + bsize_elem = PAGE_SIZE * (1 << order); + + /* Determine the actual number of sg entries needed */ + if (buf_len % bsize_elem) + num_elem = (buf_len / bsize_elem) + 1; + else + num_elem = buf_len / bsize_elem; + + /* Allocate a scatter/gather list for the DMA */ + sglist = kmalloc(sizeof(struct ipr_sglist) + + (sizeof(struct scatterlist) * (num_elem - 1)), + GFP_KERNEL); + + if (sglist == NULL) { + ipr_trace; + return NULL; + } + + memset(sglist, 0, sizeof(struct ipr_sglist) + + (sizeof(struct scatterlist) * (num_elem - 1))); + + scatterlist = sglist->scatterlist; + + sglist->order = order; + sglist->num_sg = num_elem; + + /* Allocate a bunch of sg elements */ + for (i = 0; i < num_elem; i++) { + page = alloc_pages(GFP_KERNEL, order); + if (!page) { + ipr_trace; + + /* Free up what we already allocated */ + for (j = i - 1; j >= 0; j--) + __free_pages(scatterlist[j].page, order); + kfree(sglist); + return NULL; + } + + scatterlist[i].page = page; + } + + return sglist; +} + +/** + * ipr_free_ucode_buffer - Frees a microcode download buffer + * @p_dnld: scatter/gather list pointer + * + * Free a DMA'able ucode download buffer previously allocated with + * ipr_alloc_ucode_buffer + * + * Return value: + * nothing + **/ +static void ipr_free_ucode_buffer(struct ipr_sglist *sglist) +{ + int i; + + for (i = 0; i < sglist->num_sg; i++) + __free_pages(sglist->scatterlist[i].page, sglist->order); + + kfree(sglist); +} + +/** + * ipr_copy_ucode_buffer - Copy user buffer to kernel buffer + * @sglist: scatter/gather list pointer + * @buffer: buffer pointer + * @len: buffer length + * + * Copy a microcode image from a user buffer into a buffer allocated by + * ipr_alloc_ucode_buffer + * + * Return value: + * 0 on success / other on failure + **/ +static int ipr_copy_ucode_buffer(struct ipr_sglist *sglist, + u8 *buffer, u32 len) +{ + int bsize_elem, i, result = 0; + struct scatterlist *scatterlist; + void *kaddr; + + /* Determine the actual number of bytes per element */ + bsize_elem = PAGE_SIZE * (1 << sglist->order); + + scatterlist = sglist->scatterlist; + + for (i = 0; i < (len / bsize_elem); i++, buffer += bsize_elem) { + kaddr = kmap(scatterlist[i].page); + memcpy(kaddr, buffer, bsize_elem); + kunmap(scatterlist[i].page); + + scatterlist[i].length = bsize_elem; + + if (result != 0) { + ipr_trace; + return result; + } + } + + if (len % bsize_elem) { + kaddr = kmap(scatterlist[i].page); + memcpy(kaddr, buffer, len % bsize_elem); + kunmap(scatterlist[i].page); + + scatterlist[i].length = len % bsize_elem; + } + + sglist->buffer_len = len; + return result; +} + +/** + * ipr_map_ucode_buffer - Map a microcode download buffer + * @ipr_cmd: ipr command struct + * @sglist: scatter/gather list + * @len: total length of download buffer + * + * Maps a microcode download scatter/gather list for DMA and + * builds the IOADL. + * + * Return value: + * 0 on success / -EIO on failure + **/ +static int ipr_map_ucode_buffer(struct ipr_cmnd *ipr_cmd, + struct ipr_sglist *sglist, int len) +{ + struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg; + struct ipr_ioarcb *ioarcb = &ipr_cmd->ioarcb; + struct ipr_ioadl_desc *ioadl = ipr_cmd->ioadl; + struct scatterlist *scatterlist = sglist->scatterlist; + int i; + + ipr_cmd->dma_use_sg = pci_map_sg(ioa_cfg->pdev, scatterlist, + sglist->num_sg, DMA_TO_DEVICE); + + ioarcb->cmd_pkt.flags_hi |= IPR_FLAGS_HI_WRITE_NOT_READ; + ioarcb->write_data_transfer_length = cpu_to_be32(len); + ioarcb->write_ioadl_len = + cpu_to_be32(sizeof(struct ipr_ioadl_desc) * ipr_cmd->dma_use_sg); + + for (i = 0; i < ipr_cmd->dma_use_sg; i++) { + ioadl[i].flags_and_data_len = + cpu_to_be32(IPR_IOADL_FLAGS_WRITE | sg_dma_len(&scatterlist[i])); + ioadl[i].address = + cpu_to_be32(sg_dma_address(&scatterlist[i])); + } + + if (likely(ipr_cmd->dma_use_sg)) { + ioadl[i-1].flags_and_data_len |= + cpu_to_be32(IPR_IOADL_FLAGS_LAST); + } + else { + dev_err(&ioa_cfg->pdev->dev, "pci_map_sg failed!\n"); + return -EIO; + } + + return 0; +} + +/** + * ipr_store_update_fw - Update the firmware on the adapter + * @class_dev: class_device struct + * @buf: buffer + * @count: buffer size + * + * This function will update the firmware on the adapter. + * + * Return value: + * count on success / other on failure + **/ +static ssize_t ipr_store_update_fw(struct class_device *class_dev, + const char *buf, size_t count) +{ + struct Scsi_Host *shost = class_to_shost(class_dev); + struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)shost->hostdata; + struct ipr_ucode_image_header *image_hdr; + const struct firmware *fw_entry; + struct ipr_sglist *sglist; + unsigned long lock_flags; + char fname[100]; + char *src; + int len, result, dnld_size; + + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + + len = snprintf(fname, 99, "%s", buf); + fname[len-1] = '\0'; + + if(request_firmware(&fw_entry, fname, &ioa_cfg->pdev->dev)) { + dev_err(&ioa_cfg->pdev->dev, "Firmware file %s not found\n", fname); + return -EIO; + } + + image_hdr = (struct ipr_ucode_image_header *)fw_entry->data; + + if (be32_to_cpu(image_hdr->header_length) > fw_entry->size || + (ioa_cfg->vpd_cbs->page3_data.card_type && + ioa_cfg->vpd_cbs->page3_data.card_type != image_hdr->card_type)) { + dev_err(&ioa_cfg->pdev->dev, "Invalid microcode buffer\n"); + release_firmware(fw_entry); + return -EINVAL; + } + + src = (u8 *)image_hdr + be32_to_cpu(image_hdr->header_length); + dnld_size = fw_entry->size - be32_to_cpu(image_hdr->header_length); + sglist = ipr_alloc_ucode_buffer(dnld_size); + + if (!sglist) { + dev_err(&ioa_cfg->pdev->dev, "Microcode buffer allocation failed\n"); + release_firmware(fw_entry); + return -ENOMEM; + } + + result = ipr_copy_ucode_buffer(sglist, src, dnld_size); + + if (result) { + dev_err(&ioa_cfg->pdev->dev, + "Microcode buffer copy to DMA buffer failed\n"); + ipr_free_ucode_buffer(sglist); + release_firmware(fw_entry); + return result; + } + + spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); + + if (ioa_cfg->ucode_sglist) { + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + dev_err(&ioa_cfg->pdev->dev, + "Microcode download already in progress\n"); + ipr_free_ucode_buffer(sglist); + release_firmware(fw_entry); + return -EIO; + } + + ioa_cfg->ucode_sglist = sglist; + ipr_initiate_ioa_reset(ioa_cfg, IPR_SHUTDOWN_NORMAL); + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload); + + spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); + ioa_cfg->ucode_sglist = NULL; + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + + ipr_free_ucode_buffer(sglist); + release_firmware(fw_entry); + + return count; +} + +static struct class_device_attribute ipr_update_fw_attr = { + .attr = { + .name = "update_fw", + .mode = S_IWUSR, + }, + .store = ipr_store_update_fw +}; + +static struct class_device_attribute *ipr_ioa_attrs[] = { + &ipr_fw_version_attr, + &ipr_log_level_attr, + &ipr_diagnostics_attr, + &ipr_ioa_reset_attr, + &ipr_update_fw_attr, + NULL, +}; + +#ifdef CONFIG_SCSI_IPR_DUMP +/** + * ipr_read_dump - Dump the adapter + * @kobj: kobject struct + * @buf: buffer + * @off: offset + * @count: buffer size + * + * Return value: + * number of bytes printed to buffer + **/ +static ssize_t ipr_read_dump(struct kobject *kobj, char *buf, + loff_t off, size_t count) +{ + struct class_device *cdev = container_of(kobj,struct class_device,kobj); + struct Scsi_Host *shost = class_to_shost(cdev); + struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)shost->hostdata; + struct ipr_dump *dump; + unsigned long lock_flags = 0; + char *src; + int len; + size_t rc = count; + + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + + spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); + dump = ioa_cfg->dump; + + if (ioa_cfg->sdt_state != DUMP_OBTAINED || !dump || !kobject_get(&dump->kobj)) { + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + return 0; + } + + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + + if (off > dump->driver_dump.hdr.len) { + kobject_put(&dump->kobj); + return 0; + } + + if (off + count > dump->driver_dump.hdr.len) { + count = dump->driver_dump.hdr.len - off; + rc = count; + } + + if (count && off < sizeof(dump->driver_dump)) { + if (off + count > sizeof(dump->driver_dump)) + len = sizeof(dump->driver_dump) - off; + else + len = count; + src = (u8 *)&dump->driver_dump + off; + memcpy(buf, src, len); + buf += len; + off += len; + count -= len; + } + + off -= sizeof(dump->driver_dump); + + if (count && off < offsetof(struct ipr_ioa_dump, ioa_data)) { + if (off + count > offsetof(struct ipr_ioa_dump, ioa_data)) + len = offsetof(struct ipr_ioa_dump, ioa_data) - off; + else + len = count; + src = (u8 *)&dump->ioa_dump + off; + memcpy(buf, src, len); + buf += len; + off += len; + count -= len; + } + + off -= offsetof(struct ipr_ioa_dump, ioa_data); + + while (count) { + if ((off & PAGE_MASK) != ((off + count) & PAGE_MASK)) + len = PAGE_ALIGN(off) - off; + else + len = count; + src = (u8 *)dump->ioa_dump.ioa_data[(off & PAGE_MASK) >> PAGE_SHIFT]; + src += off & ~PAGE_MASK; + memcpy(buf, src, len); + buf += len; + off += len; + count -= len; + } + + kobject_put(&dump->kobj); + return rc; +} + +/** + * ipr_release_dump - Free adapter dump memory + * @kobj: kobject struct + * + * Return value: + * nothing + **/ +static void ipr_release_dump(struct kobject *kobj) +{ + struct ipr_dump *dump = container_of(kobj,struct ipr_dump,kobj); + struct ipr_ioa_cfg *ioa_cfg = dump->ioa_cfg; + unsigned long lock_flags = 0; + int i; + + ENTER; + spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); + ioa_cfg->dump = NULL; + ioa_cfg->sdt_state = INACTIVE; + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + + for (i = 0; i < dump->ioa_dump.next_page_index; i++) + free_page((unsigned long) dump->ioa_dump.ioa_data[i]); + + kfree(dump); + LEAVE; +} + +static struct kobj_type ipr_dump_kobj_type = { + .release = ipr_release_dump, +}; + +/** + * ipr_alloc_dump - Prepare for adapter dump + * @ioa_cfg: ioa config struct + * + * Return value: + * 0 on success / other on failure + **/ +static int ipr_alloc_dump(struct ipr_ioa_cfg *ioa_cfg) +{ + struct ipr_dump *dump; + unsigned long lock_flags = 0; + + ENTER; + dump = kmalloc(sizeof(struct ipr_dump), GFP_KERNEL); + + if (!dump) { + ipr_err("Dump memory allocation failed\n"); + return -ENOMEM; + } + + memset(dump, 0, sizeof(struct ipr_dump)); + kobject_init(&dump->kobj); + dump->kobj.ktype = &ipr_dump_kobj_type; + dump->ioa_cfg = ioa_cfg; + + spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); + + if (INACTIVE != ioa_cfg->sdt_state) { + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + kfree(dump); + return 0; + } + + ioa_cfg->dump = dump; + ioa_cfg->sdt_state = WAIT_FOR_DUMP; + if (ioa_cfg->ioa_is_dead && !ioa_cfg->dump_taken) { + ioa_cfg->dump_taken = 1; + schedule_work(&ioa_cfg->work_q); + } + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + + LEAVE; + return 0; +} + +/** + * ipr_free_dump - Free adapter dump memory + * @ioa_cfg: ioa config struct + * + * Return value: + * 0 on success / other on failure + **/ +static int ipr_free_dump(struct ipr_ioa_cfg *ioa_cfg) +{ + struct ipr_dump *dump; + unsigned long lock_flags = 0; + + ENTER; + + spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); + dump = ioa_cfg->dump; + if (!dump) { + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + return 0; + } + + ioa_cfg->dump = NULL; + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + + kobject_put(&dump->kobj); + + LEAVE; + return 0; +} + +/** + * ipr_write_dump - Setup dump state of adapter + * @kobj: kobject struct + * @buf: buffer + * @off: offset + * @count: buffer size + * + * Return value: + * number of bytes printed to buffer + **/ +static ssize_t ipr_write_dump(struct kobject *kobj, char *buf, + loff_t off, size_t count) +{ + struct class_device *cdev = container_of(kobj,struct class_device,kobj); + struct Scsi_Host *shost = class_to_shost(cdev); + struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)shost->hostdata; + int rc; + + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + + if (buf[0] == '1') + rc = ipr_alloc_dump(ioa_cfg); + else if (buf[0] == '0') + rc = ipr_free_dump(ioa_cfg); + else + return -EINVAL; + + if (rc) + return rc; + else + return count; +} + +static struct bin_attribute ipr_dump_attr = { + .attr = { + .name = "dump", + .mode = S_IRUSR | S_IWUSR, + }, + .size = 0, + .read = ipr_read_dump, + .write = ipr_write_dump +}; +#else +static int ipr_free_dump(struct ipr_ioa_cfg *ioa_cfg) { return 0; }; +#endif + +/** + * ipr_store_queue_depth - Change the device's queue depth + * @dev: device struct + * @buf: buffer + * + * Return value: + * number of bytes printed to buffer + **/ +static ssize_t ipr_store_queue_depth(struct device *dev, + const char *buf, size_t count) +{ + struct scsi_device *sdev = to_scsi_device(dev); + struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)sdev->host->hostdata; + struct ipr_resource_entry *res; + int qdepth = simple_strtoul(buf, NULL, 10); + int tagged = 0; + unsigned long lock_flags = 0; + ssize_t len = -ENXIO; + + spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); + res = (struct ipr_resource_entry *)sdev->hostdata; + if (res) { + res->qdepth = qdepth; + + if (ipr_is_gscsi(res) && res->tcq_active) + tagged = MSG_ORDERED_TAG; + + len = strlen(buf); + } + + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + scsi_adjust_queue_depth(sdev, tagged, qdepth); + return len; +} + +static struct device_attribute ipr_queue_depth_attr = { + .attr = { + .name = "queue_depth", + .mode = S_IRUSR | S_IWUSR, + }, + .store = ipr_store_queue_depth +}; + +/** + * ipr_show_tcq_enable - Show if the device is enabled for tcqing + * @dev: device struct + * @buf: buffer + * + * Return value: + * number of bytes printed to buffer + **/ +static ssize_t ipr_show_tcq_enable(struct device *dev, char *buf) +{ + struct scsi_device *sdev = to_scsi_device(dev); + struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)sdev->host->hostdata; + struct ipr_resource_entry *res; + unsigned long lock_flags = 0; + ssize_t len = -ENXIO; + + spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); + res = (struct ipr_resource_entry *)sdev->hostdata; + if (res) + len = snprintf(buf, PAGE_SIZE, "%d\n", res->tcq_active); + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + return len; +} + +/** + * ipr_store_tcq_enable - Change the device's TCQing state + * @dev: device struct + * @buf: buffer + * + * Return value: + * number of bytes printed to buffer + **/ +static ssize_t ipr_store_tcq_enable(struct device *dev, + const char *buf, size_t count) +{ + struct scsi_device *sdev = to_scsi_device(dev); + struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)sdev->host->hostdata; + struct ipr_resource_entry *res; + unsigned long lock_flags = 0; + int tcq_active = simple_strtoul(buf, NULL, 10); + int qdepth = IPR_MAX_CMD_PER_LUN; + int tagged = 0; + ssize_t len = -ENXIO; + + spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); + + res = (struct ipr_resource_entry *)sdev->hostdata; + + if (res) { + res->tcq_active = 0; + qdepth = res->qdepth; + + if (ipr_is_gscsi(res) && sdev->tagged_supported) { + if (tcq_active) { + tagged = MSG_ORDERED_TAG; + res->tcq_active = 1; + } + + len = strlen(buf); + } else if (tcq_active) { + len = -EINVAL; + } + } + + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + scsi_adjust_queue_depth(sdev, tagged, qdepth); + return len; +} + +static struct device_attribute ipr_tcqing_attr = { + .attr = { + .name = "tcq_enable", + .mode = S_IRUSR | S_IWUSR, + }, + .store = ipr_store_tcq_enable, + .show = ipr_show_tcq_enable +}; + +/** + * ipr_show_adapter_handle - Show the adapter's resource handle for this device + * @dev: device struct + * @buf: buffer + * + * Return value: + * number of bytes printed to buffer + **/ +static ssize_t ipr_show_adapter_handle(struct device *dev, char *buf) +{ + struct scsi_device *sdev = to_scsi_device(dev); + struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)sdev->host->hostdata; + struct ipr_resource_entry *res; + unsigned long lock_flags = 0; + ssize_t len = -ENXIO; + + spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); + res = (struct ipr_resource_entry *)sdev->hostdata; + if (res) + len = snprintf(buf, PAGE_SIZE, "%08X\n", res->cfgte.res_handle); + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + return len; +} + +static struct device_attribute ipr_adapter_handle_attr = { + .attr = { + .name = "adapter_handle", + .mode = S_IRUSR, + }, + .show = ipr_show_adapter_handle +}; + +static struct device_attribute *ipr_dev_attrs[] = { + &ipr_queue_depth_attr, + &ipr_tcqing_attr, + &ipr_adapter_handle_attr, + NULL, +}; + +/** + * ipr_biosparam - Return the HSC mapping + * @sdev: scsi device struct + * @block_device: block device pointer + * @capacity: capacity of the device + * @parm: Array containing returned HSC values. + * + * This function generates the HSC parms that fdisk uses. + * We want to make sure we return something that places partitions + * on 4k boundaries for best performance with the IOA. + * + * Return value: + * 0 on success + **/ +static int ipr_biosparam(struct scsi_device *sdev, + struct block_device *block_device, + sector_t capacity, int *parm) +{ + int heads, sectors, cylinders; + + heads = 128; + sectors = 32; + + cylinders = capacity; + sector_div(cylinders, (128 * 32)); + + /* return result */ + parm[0] = heads; + parm[1] = sectors; + parm[2] = cylinders; + + return 0; +} + +/** + * ipr_slave_destroy - Unconfigure a SCSI device + * @sdev: scsi device struct + * + * Return value: + * nothing + **/ +static void ipr_slave_destroy(struct scsi_device *sdev) +{ + struct ipr_resource_entry *res; + struct ipr_ioa_cfg *ioa_cfg; + unsigned long lock_flags = 0; + + ioa_cfg = (struct ipr_ioa_cfg *) sdev->host->hostdata; + + spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); + res = (struct ipr_resource_entry *) sdev->hostdata; + if (res) { + sdev->hostdata = NULL; + res->sdev = NULL; + } + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); +} + +/** + * ipr_slave_configure - Configure a SCSI device + * @sdev: scsi device struct + * + * This function configures the specified scsi device. + * + * Return value: + * 0 on success + **/ +static int ipr_slave_configure(struct scsi_device *sdev) +{ + struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *) sdev->host->hostdata; + struct ipr_resource_entry *res; + unsigned long lock_flags = 0; + + spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); + res = sdev->hostdata; + if (res) { + if (ipr_is_af_dasd_device(res)) + sdev->type = TYPE_RAID; + if (ipr_is_af_dasd_device(res) || ipr_is_ioa_resource(res)) + sdev->scsi_level = 4; + if (ipr_is_vset_device(res)) + sdev->timeout = IPR_VSET_RW_TIMEOUT; + + sdev->allow_restart = 1; + scsi_adjust_queue_depth(sdev, 0, res->qdepth); + } + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + return 0; +} + +/** + * ipr_slave_alloc - Prepare for commands to a device. + * @sdev: scsi device struct + * + * This function saves a pointer to the resource entry + * in the scsi device struct if the device exists. We + * can then use this pointer in ipr_queuecommand when + * handling new commands. + * + * Return value: + * 0 on success + **/ +static int ipr_slave_alloc(struct scsi_device *sdev) +{ + struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *) sdev->host->hostdata; + struct ipr_resource_entry *res; + unsigned long lock_flags; + + sdev->hostdata = NULL; + + spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); + + list_for_each_entry(res, &ioa_cfg->used_res_q, queue) { + if ((res->cfgte.res_addr.bus == sdev->channel) && + (res->cfgte.res_addr.target == sdev->id) && + (res->cfgte.res_addr.lun == sdev->lun)) { + res->sdev = sdev; + res->add_to_ml = 0; + sdev->hostdata = res; + res->needs_sync_complete = 1; + break; + } + } + + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + + return 0; +} + +/** + * ipr_eh_host_reset - Reset the host adapter + * @scsi_cmd: scsi command struct + * + * Return value: + * SUCCESS / FAILED + **/ +static int ipr_eh_host_reset(struct scsi_cmnd * scsi_cmd) +{ + struct ipr_ioa_cfg *ioa_cfg; + int rc; + + ENTER; + ioa_cfg = (struct ipr_ioa_cfg *) scsi_cmd->device->host->hostdata; + + dev_err(&ioa_cfg->pdev->dev, + "Adapter being reset as a result of error recovery.\n"); + + if (WAIT_FOR_DUMP == ioa_cfg->sdt_state) + ioa_cfg->sdt_state = GET_DUMP; + + rc = ipr_reset_reload(ioa_cfg, IPR_SHUTDOWN_ABBREV); + + LEAVE; + return rc; +} + +/** + * ipr_eh_dev_reset - Reset the device + * @scsi_cmd: scsi command struct + * + * This function issues a device reset to the affected device. + * A LUN reset will be sent to the device first. If that does + * not work, a target reset will be sent. + * + * Return value: + * SUCCESS / FAILED + **/ +static int ipr_eh_dev_reset(struct scsi_cmnd * scsi_cmd) +{ + struct ipr_cmnd *ipr_cmd; + struct ipr_ioa_cfg *ioa_cfg; + struct ipr_resource_entry *res; + struct ipr_cmd_pkt *cmd_pkt; + u32 ioasc; + + ENTER; + ioa_cfg = (struct ipr_ioa_cfg *) scsi_cmd->device->host->hostdata; + res = scsi_cmd->device->hostdata; + + if (!res || (!ipr_is_gscsi(res) && !ipr_is_vset_device(res))) + return FAILED; + + /* + * If we are currently going through reset/reload, return failed. This will force the + * mid-layer to call ipr_eh_host_reset, which will then go to sleep and wait for the + * reset to complete + */ + if (ioa_cfg->in_reset_reload) + return FAILED; + if (ioa_cfg->ioa_is_dead) + return FAILED; + + list_for_each_entry(ipr_cmd, &ioa_cfg->pending_q, queue) { + if (ipr_cmd->ioarcb.res_handle == res->cfgte.res_handle) { + if (ipr_cmd->scsi_cmd) + ipr_cmd->done = ipr_scsi_eh_done; + } + } + + res->resetting_device = 1; + + ipr_cmd = ipr_get_free_ipr_cmnd(ioa_cfg); + + ipr_cmd->ioarcb.res_handle = res->cfgte.res_handle; + cmd_pkt = &ipr_cmd->ioarcb.cmd_pkt; + cmd_pkt->request_type = IPR_RQTYPE_IOACMD; + cmd_pkt->cdb[0] = IPR_RESET_DEVICE; + + ipr_sdev_err(scsi_cmd->device, "Resetting device\n"); + ipr_send_blocking_cmd(ipr_cmd, ipr_timeout, IPR_DEVICE_RESET_TIMEOUT); + + ioasc = be32_to_cpu(ipr_cmd->ioasa.ioasc); + + res->resetting_device = 0; + + list_add_tail(&ipr_cmd->queue, &ioa_cfg->free_q); + + LEAVE; + return (IPR_IOASC_SENSE_KEY(ioasc) ? FAILED : SUCCESS); +} + +/** + * ipr_bus_reset_done - Op done function for bus reset. + * @ipr_cmd: ipr command struct + * + * This function is the op done function for a bus reset + * + * Return value: + * none + **/ +static void ipr_bus_reset_done(struct ipr_cmnd *ipr_cmd) +{ + struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg; + struct ipr_resource_entry *res; + + ENTER; + list_for_each_entry(res, &ioa_cfg->used_res_q, queue) { + if (!memcmp(&res->cfgte.res_handle, &ipr_cmd->ioarcb.res_handle, + sizeof(res->cfgte.res_handle))) { + scsi_report_bus_reset(ioa_cfg->host, res->cfgte.res_addr.bus); + break; + } + } + + /* + * If abort has not completed, indicate the reset has, else call the + * abort's done function to wake the sleeping eh thread + */ + if (ipr_cmd->u.sibling->u.sibling) + ipr_cmd->u.sibling->u.sibling = NULL; + else + ipr_cmd->u.sibling->done(ipr_cmd->u.sibling); + + list_add_tail(&ipr_cmd->queue, &ioa_cfg->free_q); + LEAVE; +} + +/** + * ipr_abort_timeout - An abort task has timed out + * @ipr_cmd: ipr command struct + * + * This function handles when an abort task times out. If this + * happens we issue a bus reset since we have resources tied + * up that must be freed before returning to the midlayer. + * + * Return value: + * none + **/ +static void ipr_abort_timeout(struct ipr_cmnd *ipr_cmd) +{ + struct ipr_cmnd *reset_cmd; + struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg; + struct ipr_cmd_pkt *cmd_pkt; + unsigned long lock_flags = 0; + + ENTER; + spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); + if (ipr_cmd->completion.done || ioa_cfg->in_reset_reload) { + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + return; + } + + ipr_sdev_err(ipr_cmd->u.sdev, "Abort timed out. Resetting bus\n"); + reset_cmd = ipr_get_free_ipr_cmnd(ioa_cfg); + ipr_cmd->u.sibling = reset_cmd; + reset_cmd->u.sibling = ipr_cmd; + reset_cmd->ioarcb.res_handle = ipr_cmd->ioarcb.res_handle; + cmd_pkt = &reset_cmd->ioarcb.cmd_pkt; + cmd_pkt->request_type = IPR_RQTYPE_IOACMD; + cmd_pkt->cdb[0] = IPR_RESET_DEVICE; + cmd_pkt->cdb[2] = IPR_RESET_TYPE_SELECT | IPR_BUS_RESET; + + ipr_do_req(reset_cmd, ipr_bus_reset_done, ipr_timeout, IPR_DEVICE_RESET_TIMEOUT); + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + LEAVE; +} + +/** + * ipr_cancel_op - Cancel specified op + * @scsi_cmd: scsi command struct + * + * This function cancels specified op. + * + * Return value: + * SUCCESS / FAILED + **/ +static int ipr_cancel_op(struct scsi_cmnd * scsi_cmd) +{ + struct ipr_cmnd *ipr_cmd; + struct ipr_ioa_cfg *ioa_cfg; + struct ipr_resource_entry *res; + struct ipr_cmd_pkt *cmd_pkt; + u32 ioasc, ioarcb_addr; + int op_found = 0; + + ENTER; + ioa_cfg = (struct ipr_ioa_cfg *)scsi_cmd->device->host->hostdata; + res = scsi_cmd->device->hostdata; + + if (!res || (!ipr_is_gscsi(res) && !ipr_is_vset_device(res))) + return FAILED; + + list_for_each_entry(ipr_cmd, &ioa_cfg->pending_q, queue) { + if (ipr_cmd->scsi_cmd == scsi_cmd) { + ipr_cmd->done = ipr_scsi_eh_done; + op_found = 1; + break; + } + } + + if (!op_found) + return SUCCESS; + + ioarcb_addr = be32_to_cpu(ipr_cmd->ioarcb.ioarcb_host_pci_addr); + + ipr_cmd = ipr_get_free_ipr_cmnd(ioa_cfg); + ipr_cmd->ioarcb.res_handle = res->cfgte.res_handle; + cmd_pkt = &ipr_cmd->ioarcb.cmd_pkt; + cmd_pkt->request_type = IPR_RQTYPE_IOACMD; + cmd_pkt->cdb[0] = IPR_ABORT_TASK; + cmd_pkt->cdb[2] = (ioarcb_addr >> 24) & 0xff; + cmd_pkt->cdb[3] = (ioarcb_addr >> 16) & 0xff; + cmd_pkt->cdb[4] = (ioarcb_addr >> 8) & 0xff; + cmd_pkt->cdb[5] = ioarcb_addr & 0xff; + ipr_cmd->u.sdev = scsi_cmd->device; + + ipr_sdev_err(scsi_cmd->device, "Aborting command: %02X\n", scsi_cmd->cmnd[0]); + ipr_send_blocking_cmd(ipr_cmd, ipr_abort_timeout, IPR_ABORT_TASK_TIMEOUT); + ioasc = be32_to_cpu(ipr_cmd->ioasa.ioasc); + + /* + * If the abort task timed out and we sent a bus reset, we will get + * one the following responses to the abort + */ + if (ioasc == IPR_IOASC_BUS_WAS_RESET || ioasc == IPR_IOASC_SYNC_REQUIRED) { + ioasc = 0; + ipr_trace; + } + + list_add_tail(&ipr_cmd->queue, &ioa_cfg->free_q); + res->needs_sync_complete = 1; + + LEAVE; + return (IPR_IOASC_SENSE_KEY(ioasc) ? FAILED : SUCCESS); +} + +/** + * ipr_eh_abort - Abort a single op + * @scsi_cmd: scsi command struct + * + * Return value: + * SUCCESS / FAILED + **/ +static int ipr_eh_abort(struct scsi_cmnd * scsi_cmd) +{ + struct ipr_ioa_cfg *ioa_cfg; + + ENTER; + ioa_cfg = (struct ipr_ioa_cfg *) scsi_cmd->device->host->hostdata; + + /* If we are currently going through reset/reload, return failed. This will force the + mid-layer to call ipr_eh_host_reset, which will then go to sleep and wait for the + reset to complete */ + if (ioa_cfg->in_reset_reload) + return FAILED; + if (ioa_cfg->ioa_is_dead) + return FAILED; + if (!scsi_cmd->device->hostdata) + return FAILED; + + LEAVE; + return ipr_cancel_op(scsi_cmd); +} + +/** + * ipr_handle_other_interrupt - Handle "other" interrupts + * @ioa_cfg: ioa config struct + * @int_reg: interrupt register + * + * Return value: + * IRQ_NONE / IRQ_HANDLED + **/ +static irqreturn_t ipr_handle_other_interrupt(struct ipr_ioa_cfg *ioa_cfg, + volatile u32 int_reg) +{ + irqreturn_t rc = IRQ_HANDLED; + + if (int_reg & IPR_PCII_IOA_TRANS_TO_OPER) { + /* Mask the interrupt */ + writel(IPR_PCII_IOA_TRANS_TO_OPER, ioa_cfg->regs.set_interrupt_mask_reg); + + /* Clear the interrupt */ + writel(IPR_PCII_IOA_TRANS_TO_OPER, ioa_cfg->regs.clr_interrupt_reg); + int_reg = readl(ioa_cfg->regs.sense_interrupt_reg); + + list_del(&ioa_cfg->reset_cmd->queue); + del_timer(&ioa_cfg->reset_cmd->timer); + ipr_reset_ioa_job(ioa_cfg->reset_cmd); + } else { + if (int_reg & IPR_PCII_IOA_UNIT_CHECKED) + ioa_cfg->ioa_unit_checked = 1; + else + dev_err(&ioa_cfg->pdev->dev, + "Permanent IOA failure. 0x%08X\n", int_reg); + + if (WAIT_FOR_DUMP == ioa_cfg->sdt_state) + ioa_cfg->sdt_state = GET_DUMP; + + ipr_mask_and_clear_interrupts(ioa_cfg, ~0); + ipr_initiate_ioa_reset(ioa_cfg, IPR_SHUTDOWN_NONE); + } + + return rc; +} + +/** + * ipr_isr - Interrupt service routine + * @irq: irq number + * @devp: pointer to ioa config struct + * @regs: pt_regs struct + * + * Return value: + * IRQ_NONE / IRQ_HANDLED + **/ +static irqreturn_t ipr_isr(int irq, void *devp, struct pt_regs *regs) +{ + struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)devp; + unsigned long lock_flags = 0; + volatile u32 int_reg, int_mask_reg; + u32 ioasc; + u16 cmd_index; + struct ipr_cmnd *ipr_cmd; + irqreturn_t rc = IRQ_NONE; + + spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); + + /* If interrupts are disabled, ignore the interrupt */ + if (!ioa_cfg->allow_interrupts) { + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + return IRQ_NONE; + } + + int_mask_reg = readl(ioa_cfg->regs.sense_interrupt_mask_reg); + int_reg = readl(ioa_cfg->regs.sense_interrupt_reg) & ~int_mask_reg; + + /* If an interrupt on the adapter did not occur, ignore it */ + if (unlikely((int_reg & IPR_PCII_OPER_INTERRUPTS) == 0)) { + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + return IRQ_NONE; + } + + while (1) { + ipr_cmd = NULL; + + while ((be32_to_cpu(*ioa_cfg->hrrq_curr) & IPR_HRRQ_TOGGLE_BIT) == + ioa_cfg->toggle_bit) { + + cmd_index = (be32_to_cpu(*ioa_cfg->hrrq_curr) & + IPR_HRRQ_REQ_RESP_HANDLE_MASK) >> IPR_HRRQ_REQ_RESP_HANDLE_SHIFT; + + if (unlikely(cmd_index >= IPR_NUM_CMD_BLKS)) { + ioa_cfg->errors_logged++; + dev_err(&ioa_cfg->pdev->dev, "Invalid response handle from IOA\n"); + + if (WAIT_FOR_DUMP == ioa_cfg->sdt_state) + ioa_cfg->sdt_state = GET_DUMP; + + ipr_initiate_ioa_reset(ioa_cfg, IPR_SHUTDOWN_NONE); + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + return IRQ_HANDLED; + } + + ipr_cmd = ioa_cfg->ipr_cmnd_list[cmd_index]; + + ioasc = be32_to_cpu(ipr_cmd->ioasa.ioasc); + + ipr_trc_hook(ipr_cmd, IPR_TRACE_FINISH, ioasc); + + list_del(&ipr_cmd->queue); + del_timer(&ipr_cmd->timer); + ipr_cmd->done(ipr_cmd); + + rc = IRQ_HANDLED; + + if (ioa_cfg->hrrq_curr < ioa_cfg->hrrq_end) { + ioa_cfg->hrrq_curr++; + } else { + ioa_cfg->hrrq_curr = ioa_cfg->hrrq_start; + ioa_cfg->toggle_bit ^= 1u; + } + } + + if (ipr_cmd != NULL) { + /* Clear the PCI interrupt */ + writel(IPR_PCII_HRRQ_UPDATED, ioa_cfg->regs.clr_interrupt_reg); + int_reg = readl(ioa_cfg->regs.sense_interrupt_reg) & ~int_mask_reg; + } else + break; + } + + if (unlikely(rc == IRQ_NONE)) + rc = ipr_handle_other_interrupt(ioa_cfg, int_reg); + + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + return rc; +} + +/** + * ipr_build_ioadl - Build a scatter/gather list and map the buffer + * @ioa_cfg: ioa config struct + * @ipr_cmd: ipr command struct + * + * Return value: + * 0 on success / -1 on failure + **/ +static int ipr_build_ioadl(struct ipr_ioa_cfg *ioa_cfg, + struct ipr_cmnd *ipr_cmd) +{ + int i; + struct scatterlist *sglist; + u32 length; + u32 ioadl_flags = 0; + struct scsi_cmnd *scsi_cmd = ipr_cmd->scsi_cmd; + struct ipr_ioarcb *ioarcb = &ipr_cmd->ioarcb; + struct ipr_ioadl_desc *ioadl = ipr_cmd->ioadl; + + length = scsi_cmd->request_bufflen; + + if (length == 0) + return 0; + + if (scsi_cmd->use_sg) { + ipr_cmd->dma_use_sg = pci_map_sg(ioa_cfg->pdev, + scsi_cmd->request_buffer, + scsi_cmd->use_sg, + scsi_cmd->sc_data_direction); + + if (scsi_cmd->sc_data_direction == DMA_TO_DEVICE) { + ioadl_flags = IPR_IOADL_FLAGS_WRITE; + ioarcb->cmd_pkt.flags_hi |= IPR_FLAGS_HI_WRITE_NOT_READ; + ioarcb->write_data_transfer_length = cpu_to_be32(length); + ioarcb->write_ioadl_len = + cpu_to_be32(sizeof(struct ipr_ioadl_desc) * ipr_cmd->dma_use_sg); + } else if (scsi_cmd->sc_data_direction == DMA_FROM_DEVICE) { + ioadl_flags = IPR_IOADL_FLAGS_READ; + ioarcb->read_data_transfer_length = cpu_to_be32(length); + ioarcb->read_ioadl_len = + cpu_to_be32(sizeof(struct ipr_ioadl_desc) * ipr_cmd->dma_use_sg); + } + + sglist = scsi_cmd->request_buffer; + + for (i = 0; i < ipr_cmd->dma_use_sg; i++) { + ioadl[i].flags_and_data_len = + cpu_to_be32(ioadl_flags | sg_dma_len(&sglist[i])); + ioadl[i].address = + cpu_to_be32(sg_dma_address(&sglist[i])); + } + + if (likely(ipr_cmd->dma_use_sg)) { + ioadl[i-1].flags_and_data_len |= + cpu_to_be32(IPR_IOADL_FLAGS_LAST); + return 0; + } else + dev_err(&ioa_cfg->pdev->dev, "pci_map_sg failed!\n"); + } else { + if (scsi_cmd->sc_data_direction == DMA_TO_DEVICE) { + ioadl_flags = IPR_IOADL_FLAGS_WRITE; + ioarcb->cmd_pkt.flags_hi |= IPR_FLAGS_HI_WRITE_NOT_READ; + ioarcb->write_data_transfer_length = cpu_to_be32(length); + ioarcb->write_ioadl_len = cpu_to_be32(sizeof(struct ipr_ioadl_desc)); + } else if (scsi_cmd->sc_data_direction == DMA_FROM_DEVICE) { + ioadl_flags = IPR_IOADL_FLAGS_READ; + ioarcb->read_data_transfer_length = cpu_to_be32(length); + ioarcb->read_ioadl_len = cpu_to_be32(sizeof(struct ipr_ioadl_desc)); + } + + ipr_cmd->dma_handle = pci_map_single(ioa_cfg->pdev, + scsi_cmd->request_buffer, length, + scsi_cmd->sc_data_direction); + + if (likely(!pci_dma_mapping_error(ipr_cmd->dma_handle))) { + ipr_cmd->dma_use_sg = 1; + ioadl[0].flags_and_data_len = + cpu_to_be32(ioadl_flags | length | IPR_IOADL_FLAGS_LAST); + ioadl[0].address = cpu_to_be32(ipr_cmd->dma_handle); + return 0; + } else + dev_err(&ioa_cfg->pdev->dev, "pci_map_single failed!\n"); + } + + return -1; +} + +/** + * ipr_get_task_attributes - Translate SPI Q-Tag to task attributes + * @scsi_cmd: scsi command struct + * + * Return value: + * task attributes + **/ +static u8 ipr_get_task_attributes(struct scsi_cmnd *scsi_cmd) +{ + u8 tag[2]; + u8 rc = IPR_FLAGS_LO_UNTAGGED_TASK; + + if (scsi_populate_tag_msg(scsi_cmd, tag)) { + switch (tag[0]) { + case MSG_SIMPLE_TAG: + rc = IPR_FLAGS_LO_SIMPLE_TASK; + break; + case MSG_HEAD_TAG: + rc = IPR_FLAGS_LO_HEAD_OF_Q_TASK; + break; + case MSG_ORDERED_TAG: + rc = IPR_FLAGS_LO_ORDERED_TASK; + break; + }; + } + + return rc; +} + +/** + * ipr_erp_done - Process completion of ERP for a device + * @ipr_cmd: ipr command struct + * + * This function copies the sense buffer into the scsi_cmd + * struct and pushes the scsi_done function. + * + * Return value: + * nothing + **/ +static void ipr_erp_done(struct ipr_cmnd *ipr_cmd) +{ + struct scsi_cmnd *scsi_cmd = ipr_cmd->scsi_cmd; + struct ipr_resource_entry *res = scsi_cmd->device->hostdata; + struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg; + u32 ioasc = be32_to_cpu(ipr_cmd->ioasa.ioasc); + + if (IPR_IOASC_SENSE_KEY(ioasc) > 0) { + scsi_cmd->result |= (DID_ERROR << 16); + ipr_sdev_err(scsi_cmd->device, + "Request Sense failed with IOASC: 0x%08X\n", ioasc); + } else { + memcpy(scsi_cmd->sense_buffer, ipr_cmd->sense_buffer, + SCSI_SENSE_BUFFERSIZE); + } + + if (res) + res->needs_sync_complete = 1; + ipr_unmap_sglist(ioa_cfg, ipr_cmd); + list_add_tail(&ipr_cmd->queue, &ioa_cfg->free_q); + scsi_cmd->scsi_done(scsi_cmd); +} + +/** + * ipr_reinit_ipr_cmnd_for_erp - Re-initialize a cmnd block to be used for ERP + * @ipr_cmd: ipr command struct + * + * Return value: + * none + **/ +static void ipr_reinit_ipr_cmnd_for_erp(struct ipr_cmnd *ipr_cmd) +{ + struct ipr_ioarcb *ioarcb; + struct ipr_ioasa *ioasa; + + ioarcb = &ipr_cmd->ioarcb; + ioasa = &ipr_cmd->ioasa; + + memset(&ioarcb->cmd_pkt, 0, sizeof(struct ipr_cmd_pkt)); + ioarcb->write_data_transfer_length = 0; + ioarcb->read_data_transfer_length = 0; + ioarcb->write_ioadl_len = 0; + ioarcb->read_ioadl_len = 0; + ioasa->ioasc = 0; + ioasa->residual_data_len = 0; +} + +/** + * ipr_erp_request_sense - Send request sense to a device + * @ipr_cmd: ipr command struct + * + * This function sends a request sense to a device as a result + * of a check condition. + * + * Return value: + * nothing + **/ +static void ipr_erp_request_sense(struct ipr_cmnd *ipr_cmd) +{ + struct ipr_cmd_pkt *cmd_pkt = &ipr_cmd->ioarcb.cmd_pkt; + + ipr_reinit_ipr_cmnd_for_erp(ipr_cmd); + + cmd_pkt->request_type = IPR_RQTYPE_SCSICDB; + cmd_pkt->cdb[0] = REQUEST_SENSE; + cmd_pkt->cdb[4] = SCSI_SENSE_BUFFERSIZE; + cmd_pkt->flags_hi |= IPR_FLAGS_HI_SYNC_OVERRIDE; + cmd_pkt->flags_hi |= IPR_FLAGS_HI_NO_ULEN_CHK; + cmd_pkt->timeout = cpu_to_be16(IPR_REQUEST_SENSE_TIMEOUT / HZ); + + ipr_cmd->ioadl[0].flags_and_data_len = + cpu_to_be32(IPR_IOADL_FLAGS_READ_LAST | SCSI_SENSE_BUFFERSIZE); + ipr_cmd->ioadl[0].address = + cpu_to_be32(ipr_cmd->sense_buffer_dma); + + ipr_cmd->ioarcb.read_ioadl_len = + cpu_to_be32(sizeof(struct ipr_ioadl_desc)); + ipr_cmd->ioarcb.read_data_transfer_length = + cpu_to_be32(SCSI_SENSE_BUFFERSIZE); + + ipr_do_req(ipr_cmd, ipr_erp_done, ipr_timeout, + IPR_REQUEST_SENSE_TIMEOUT * 2); +} + +/** + * ipr_erp_cancel_all - Send cancel all to a device + * @ipr_cmd: ipr command struct + * + * This function sends a cancel all to a device to clear the + * queue. If we are running TCQ on the device, QERR is set to 1, + * which means all outstanding ops have been dropped on the floor. + * Cancel all will return them to us. + * + * Return value: + * nothing + **/ +static void ipr_erp_cancel_all(struct ipr_cmnd *ipr_cmd) +{ + struct scsi_cmnd *scsi_cmd = ipr_cmd->scsi_cmd; + struct ipr_resource_entry *res = scsi_cmd->device->hostdata; + struct ipr_cmd_pkt *cmd_pkt; + + res->in_erp = 1; + + ipr_reinit_ipr_cmnd_for_erp(ipr_cmd); + + cmd_pkt = &ipr_cmd->ioarcb.cmd_pkt; + cmd_pkt->request_type = IPR_RQTYPE_IOACMD; + cmd_pkt->cdb[0] = IPR_CANCEL_ALL_REQUESTS; + + ipr_do_req(ipr_cmd, ipr_erp_request_sense, ipr_timeout, + IPR_CANCEL_ALL_TIMEOUT); +} + +/** + * ipr_dump_ioasa - Dump contents of IOASA + * @ioa_cfg: ioa config struct + * @ipr_cmd: ipr command struct + * + * This function is invoked by the interrupt handler when ops + * fail. It will log the IOASA if appropriate. Only called + * for GPDD ops. + * + * Return value: + * none + **/ +static void ipr_dump_ioasa(struct ipr_ioa_cfg *ioa_cfg, + struct ipr_cmnd *ipr_cmd) +{ + int i; + u16 data_len; + u32 ioasc; + struct ipr_ioasa *ioasa = &ipr_cmd->ioasa; + u32 *ioasa_data = (u32 *)ioasa; + int error_index; + + ioasc = be32_to_cpu(ioasa->ioasc) & IPR_IOASC_IOASC_MASK; + + if (0 == ioasc) + return; + + if (ioa_cfg->log_level < IPR_DEFAULT_LOG_LEVEL) + return; + + error_index = ipr_get_error(ioasc); + + if (ioa_cfg->log_level < IPR_MAX_LOG_LEVEL) { + /* Don't log an error if the IOA already logged one */ + if (ioasa->ilid != 0) + return; + + if (ipr_error_table[error_index].log_ioasa == 0) + return; + } + + ipr_sdev_err(ipr_cmd->scsi_cmd->device, "%s\n", + ipr_error_table[error_index].error); + + if ((ioasa->u.gpdd.end_state <= ARRAY_SIZE(ipr_gpdd_dev_end_states)) && + (ioasa->u.gpdd.bus_phase <= ARRAY_SIZE(ipr_gpdd_dev_bus_phases))) { + ipr_sdev_err(ipr_cmd->scsi_cmd->device, + "Device End state: %s Phase: %s\n", + ipr_gpdd_dev_end_states[ioasa->u.gpdd.end_state], + ipr_gpdd_dev_bus_phases[ioasa->u.gpdd.bus_phase]); + } + + if (sizeof(struct ipr_ioasa) < be16_to_cpu(ioasa->ret_stat_len)) + data_len = sizeof(struct ipr_ioasa); + else + data_len = be16_to_cpu(ioasa->ret_stat_len); + + ipr_err("IOASA Dump:\n"); + + for (i = 0; i < data_len / 4; i += 4) { + ipr_err("%08X: %08X %08X %08X %08X\n", i*4, + be32_to_cpu(ioasa_data[i]), + be32_to_cpu(ioasa_data[i+1]), + be32_to_cpu(ioasa_data[i+2]), + be32_to_cpu(ioasa_data[i+3])); + } +} + +/** + * ipr_gen_sense - Generate SCSI sense data from an IOASA + * @ioasa: IOASA + * @sense_buf: sense data buffer + * + * Return value: + * none + **/ +static void ipr_gen_sense(struct ipr_cmnd *ipr_cmd) +{ + u32 failing_lba; + u8 *sense_buf = ipr_cmd->scsi_cmd->sense_buffer; + struct ipr_resource_entry *res = ipr_cmd->scsi_cmd->device->hostdata; + struct ipr_ioasa *ioasa = &ipr_cmd->ioasa; + u32 ioasc = be32_to_cpu(ioasa->ioasc); + + memset(sense_buf, 0, SCSI_SENSE_BUFFERSIZE); + + if (ioasc >= IPR_FIRST_DRIVER_IOASC) + return; + + ipr_cmd->scsi_cmd->result = SAM_STAT_CHECK_CONDITION; + + if (ipr_is_vset_device(res) && + ioasc == IPR_IOASC_MED_DO_NOT_REALLOC && + ioasa->u.vset.failing_lba_hi != 0) { + sense_buf[0] = 0x72; + sense_buf[1] = IPR_IOASC_SENSE_KEY(ioasc); + sense_buf[2] = IPR_IOASC_SENSE_CODE(ioasc); + sense_buf[3] = IPR_IOASC_SENSE_QUAL(ioasc); + + sense_buf[7] = 12; + sense_buf[8] = 0; + sense_buf[9] = 0x0A; + sense_buf[10] = 0x80; + + failing_lba = be32_to_cpu(ioasa->u.vset.failing_lba_hi); + + sense_buf[12] = (failing_lba & 0xff000000) >> 24; + sense_buf[13] = (failing_lba & 0x00ff0000) >> 16; + sense_buf[14] = (failing_lba & 0x0000ff00) >> 8; + sense_buf[15] = failing_lba & 0x000000ff; + + failing_lba = be32_to_cpu(ioasa->u.vset.failing_lba_lo); + + sense_buf[16] = (failing_lba & 0xff000000) >> 24; + sense_buf[17] = (failing_lba & 0x00ff0000) >> 16; + sense_buf[18] = (failing_lba & 0x0000ff00) >> 8; + sense_buf[19] = failing_lba & 0x000000ff; + } else { + sense_buf[0] = 0x70; + sense_buf[2] = IPR_IOASC_SENSE_KEY(ioasc); + sense_buf[12] = IPR_IOASC_SENSE_CODE(ioasc); + sense_buf[13] = IPR_IOASC_SENSE_QUAL(ioasc); + + /* Illegal request */ + if ((IPR_IOASC_SENSE_KEY(ioasc) == 0x05) && + (be32_to_cpu(ioasa->ioasc_specific) & IPR_FIELD_POINTER_VALID)) { + sense_buf[7] = 10; /* additional length */ + + /* IOARCB was in error */ + if (IPR_IOASC_SENSE_CODE(ioasc) == 0x24) + sense_buf[15] = 0xC0; + else /* Parameter data was invalid */ + sense_buf[15] = 0x80; + + sense_buf[16] = + ((IPR_FIELD_POINTER_MASK & + be32_to_cpu(ioasa->ioasc_specific)) >> 8) & 0xff; + sense_buf[17] = + (IPR_FIELD_POINTER_MASK & + be32_to_cpu(ioasa->ioasc_specific)) & 0xff; + } else { + if (ioasc == IPR_IOASC_MED_DO_NOT_REALLOC) { + if (ipr_is_vset_device(res)) + failing_lba = be32_to_cpu(ioasa->u.vset.failing_lba_lo); + else + failing_lba = be32_to_cpu(ioasa->u.dasd.failing_lba); + + sense_buf[0] |= 0x80; /* Or in the Valid bit */ + sense_buf[3] = (failing_lba & 0xff000000) >> 24; + sense_buf[4] = (failing_lba & 0x00ff0000) >> 16; + sense_buf[5] = (failing_lba & 0x0000ff00) >> 8; + sense_buf[6] = failing_lba & 0x000000ff; + } + + sense_buf[7] = 6; /* additional length */ + } + } +} + +/** + * ipr_erp_start - Process an error response for a SCSI op + * @ioa_cfg: ioa config struct + * @ipr_cmd: ipr command struct + * + * This function determines whether or not to initiate ERP + * on the affected device. + * + * Return value: + * nothing + **/ +static void ipr_erp_start(struct ipr_ioa_cfg *ioa_cfg, + struct ipr_cmnd *ipr_cmd) +{ + struct scsi_cmnd *scsi_cmd = ipr_cmd->scsi_cmd; + struct ipr_resource_entry *res = scsi_cmd->device->hostdata; + u32 ioasc = be32_to_cpu(ipr_cmd->ioasa.ioasc); + + if (!res) { + ipr_scsi_eh_done(ipr_cmd); + return; + } + + if (ipr_is_gscsi(res)) + ipr_dump_ioasa(ioa_cfg, ipr_cmd); + else + ipr_gen_sense(ipr_cmd); + + switch (ioasc & IPR_IOASC_IOASC_MASK) { + case IPR_IOASC_ABORTED_CMD_TERM_BY_HOST: + scsi_cmd->result |= (DID_ERROR << 16); + break; + case IPR_IOASC_IR_RESOURCE_HANDLE: + scsi_cmd->result |= (DID_NO_CONNECT << 16); + break; + case IPR_IOASC_HW_SEL_TIMEOUT: + scsi_cmd->result |= (DID_NO_CONNECT << 16); + res->needs_sync_complete = 1; + break; + case IPR_IOASC_SYNC_REQUIRED: + if (!res->in_erp) + res->needs_sync_complete = 1; + scsi_cmd->result |= (DID_IMM_RETRY << 16); + break; + case IPR_IOASC_MED_DO_NOT_REALLOC: /* prevent retries */ + scsi_cmd->result |= (DID_PASSTHROUGH << 16); + break; + case IPR_IOASC_BUS_WAS_RESET: + case IPR_IOASC_BUS_WAS_RESET_BY_OTHER: + /* + * Report the bus reset and ask for a retry. The device + * will give CC/UA the next command. + */ + if (!res->resetting_device) + scsi_report_bus_reset(ioa_cfg->host, scsi_cmd->device->channel); + scsi_cmd->result |= (DID_ERROR << 16); + res->needs_sync_complete = 1; + break; + case IPR_IOASC_HW_DEV_BUS_STATUS: + scsi_cmd->result |= IPR_IOASC_SENSE_STATUS(ioasc); + if (IPR_IOASC_SENSE_STATUS(ioasc) == SAM_STAT_CHECK_CONDITION) { + ipr_erp_cancel_all(ipr_cmd); + return; + } + break; + case IPR_IOASC_NR_INIT_CMD_REQUIRED: + break; + default: + scsi_cmd->result |= (DID_ERROR << 16); + if (!ipr_is_vset_device(res)) + res->needs_sync_complete = 1; + break; + } + + ipr_unmap_sglist(ioa_cfg, ipr_cmd); + list_add_tail(&ipr_cmd->queue, &ioa_cfg->free_q); + scsi_cmd->scsi_done(scsi_cmd); +} + +/** + * ipr_scsi_done - mid-layer done function + * @ipr_cmd: ipr command struct + * + * This function is invoked by the interrupt handler for + * ops generated by the SCSI mid-layer + * + * Return value: + * none + **/ +static void ipr_scsi_done(struct ipr_cmnd *ipr_cmd) +{ + struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg; + struct scsi_cmnd *scsi_cmd = ipr_cmd->scsi_cmd; + u32 ioasc = be32_to_cpu(ipr_cmd->ioasa.ioasc); + + scsi_cmd->resid = be32_to_cpu(ipr_cmd->ioasa.residual_data_len); + + if (likely(IPR_IOASC_SENSE_KEY(ioasc) == 0)) { + ipr_unmap_sglist(ioa_cfg, ipr_cmd); + list_add_tail(&ipr_cmd->queue, &ioa_cfg->free_q); + scsi_cmd->scsi_done(scsi_cmd); + } else + ipr_erp_start(ioa_cfg, ipr_cmd); +} + +/** + * ipr_save_ioafp_mode_select - Save adapters mode select data + * @ioa_cfg: ioa config struct + * @scsi_cmd: scsi command struct + * + * This function saves mode select data for the adapter to + * use following an adapter reset. + * + * Return value: + * 0 on success / SCSI_MLQUEUE_HOST_BUSY on failure + **/ +static int ipr_save_ioafp_mode_select(struct ipr_ioa_cfg *ioa_cfg, + struct scsi_cmnd *scsi_cmd) +{ + if (!ioa_cfg->saved_mode_pages) { + ioa_cfg->saved_mode_pages = kmalloc(sizeof(struct ipr_mode_pages), + GFP_ATOMIC); + if (!ioa_cfg->saved_mode_pages) { + dev_err(&ioa_cfg->pdev->dev, + "IOA mode select buffer allocation failed\n"); + return SCSI_MLQUEUE_HOST_BUSY; + } + } + + memcpy(ioa_cfg->saved_mode_pages, scsi_cmd->buffer, scsi_cmd->cmnd[4]); + ioa_cfg->saved_mode_page_len = scsi_cmd->cmnd[4]; + return 0; +} + +/** + * ipr_queuecommand - Queue a mid-layer request + * @scsi_cmd: scsi command struct + * @done: done function + * + * This function queues a request generated by the mid-layer. + * + * Return value: + * 0 on success + * SCSI_MLQUEUE_DEVICE_BUSY if device is busy + * SCSI_MLQUEUE_HOST_BUSY if host is busy + **/ +static int ipr_queuecommand(struct scsi_cmnd *scsi_cmd, + void (*done) (struct scsi_cmnd *)) +{ + struct ipr_ioa_cfg *ioa_cfg; + struct ipr_resource_entry *res; + struct ipr_ioarcb *ioarcb; + struct ipr_cmnd *ipr_cmd; + int rc = 0; + + scsi_cmd->scsi_done = done; + ioa_cfg = (struct ipr_ioa_cfg *)scsi_cmd->device->host->hostdata; + res = scsi_cmd->device->hostdata; + scsi_cmd->result = (DID_OK << 16); + + /* + * We are currently blocking all devices due to a host reset + * We have told the host to stop giving us new requests, but + * ERP ops don't count. FIXME + */ + if (unlikely(!ioa_cfg->allow_cmds)) + return SCSI_MLQUEUE_HOST_BUSY; + + /* + * FIXME - Create scsi_set_host_offline interface + * and the ioa_is_dead check can be removed + */ + if (unlikely(ioa_cfg->ioa_is_dead || !res)) { + memset(scsi_cmd->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE); + scsi_cmd->result = (DID_NO_CONNECT << 16); + scsi_cmd->scsi_done(scsi_cmd); + return 0; + } + + ipr_cmd = ipr_get_free_ipr_cmnd(ioa_cfg); + ioarcb = &ipr_cmd->ioarcb; + list_add_tail(&ipr_cmd->queue, &ioa_cfg->pending_q); + + memcpy(ioarcb->cmd_pkt.cdb, scsi_cmd->cmnd, scsi_cmd->cmd_len); + ipr_cmd->scsi_cmd = scsi_cmd; + ioarcb->res_handle = res->cfgte.res_handle; + ipr_cmd->done = ipr_scsi_done; + ipr_trc_hook(ipr_cmd, IPR_TRACE_START, IPR_GET_PHYS_LOC(res->cfgte.res_addr)); + + if (ipr_is_gscsi(res) || ipr_is_vset_device(res)) { + if (scsi_cmd->underflow == 0) + ioarcb->cmd_pkt.flags_hi |= IPR_FLAGS_HI_NO_ULEN_CHK; + + if (res->needs_sync_complete) { + ioarcb->cmd_pkt.flags_hi |= IPR_FLAGS_HI_SYNC_COMPLETE; + res->needs_sync_complete = 0; + } + + ioarcb->cmd_pkt.flags_hi |= IPR_FLAGS_HI_NO_LINK_DESC; + ioarcb->cmd_pkt.flags_lo |= IPR_FLAGS_LO_DELAY_AFTER_RST; + ioarcb->cmd_pkt.flags_lo |= IPR_FLAGS_LO_ALIGNED_BFR; + ioarcb->cmd_pkt.flags_lo |= ipr_get_task_attributes(scsi_cmd); + } + + if (!ipr_is_gscsi(res) && scsi_cmd->cmnd[0] >= 0xC0) + ioarcb->cmd_pkt.request_type = IPR_RQTYPE_IOACMD; + + if (ipr_is_ioa_resource(res) && scsi_cmd->cmnd[0] == MODE_SELECT) + rc = ipr_save_ioafp_mode_select(ioa_cfg, scsi_cmd); + + if (likely(rc == 0)) + rc = ipr_build_ioadl(ioa_cfg, ipr_cmd); + + if (likely(rc == 0)) { + mb(); + writel(be32_to_cpu(ipr_cmd->ioarcb.ioarcb_host_pci_addr), + ioa_cfg->regs.ioarrin_reg); + } else { + list_move_tail(&ipr_cmd->queue, &ioa_cfg->free_q); + return SCSI_MLQUEUE_HOST_BUSY; + } + + return 0; +} + +/** + * ipr_info - Get information about the card/driver + * @scsi_host: scsi host struct + * + * Return value: + * pointer to buffer with description string + **/ +static const char * ipr_ioa_info(struct Scsi_Host *host) +{ + static char buffer[512]; + struct ipr_ioa_cfg *ioa_cfg; + unsigned long lock_flags = 0; + + ioa_cfg = (struct ipr_ioa_cfg *) host->hostdata; + + spin_lock_irqsave(host->host_lock, lock_flags); + sprintf(buffer, "IBM %X Storage Adapter", ioa_cfg->type); + spin_unlock_irqrestore(host->host_lock, lock_flags); + + return buffer; +} + +static struct scsi_host_template driver_template = { + .module = THIS_MODULE, + .name = "IPR", + .info = ipr_ioa_info, + .queuecommand = ipr_queuecommand, + .eh_abort_handler = ipr_eh_abort, + .eh_device_reset_handler = ipr_eh_dev_reset, + .eh_host_reset_handler = ipr_eh_host_reset, + .slave_alloc = ipr_slave_alloc, + .slave_configure = ipr_slave_configure, + .slave_destroy = ipr_slave_destroy, + .bios_param = ipr_biosparam, + .can_queue = IPR_MAX_COMMANDS, + .this_id = -1, + .sg_tablesize = IPR_MAX_SGLIST, + .max_sectors = IPR_MAX_SECTORS, + .cmd_per_lun = IPR_MAX_CMD_PER_LUN, + .use_clustering = ENABLE_CLUSTERING, + .shost_attrs = ipr_ioa_attrs, + .sdev_attrs = ipr_dev_attrs, + .proc_name = IPR_NAME +}; + +#ifdef CONFIG_PPC_PSERIES +static const u16 ipr_blocked_processors[] = { + PV_NORTHSTAR, + PV_PULSAR, + PV_POWER4, + PV_ICESTAR, + PV_SSTAR, + PV_POWER4p, + PV_630, + PV_630p +}; + +/** + * ipr_invalid_adapter - Determine if this adapter is supported on this hardware + * @ioa_cfg: ioa cfg struct + * + * Adapters that use Gemstone revision < 3.1 do not work reliably on + * certain pSeries hardware. This function determines if the given + * adapter is in one of these confgurations or not. + * + * Return value: + * 1 if adapter is not supported / 0 if adapter is supported + **/ +static int ipr_invalid_adapter(struct ipr_ioa_cfg *ioa_cfg) +{ + u8 rev_id; + int i; + + if (ioa_cfg->type == 0x5702) { + if (pci_read_config_byte(ioa_cfg->pdev, PCI_REVISION_ID, + &rev_id) == PCIBIOS_SUCCESSFUL) { + if (rev_id < 4) { + for (i = 0; i < ARRAY_SIZE(ipr_blocked_processors); i++){ + if (__is_processor(ipr_blocked_processors[i])) + return 1; + } + } + } + } + return 0; +} +#else +#define ipr_invalid_adapter(ioa_cfg) 0 +#endif + +/** + * ipr_ioa_bringdown_done - IOA bring down completion. + * @ipr_cmd: ipr command struct + * + * This function processes the completion of an adapter bring down. + * It wakes any reset sleepers. + * + * Return value: + * IPR_RC_JOB_RETURN + **/ +static int ipr_ioa_bringdown_done(struct ipr_cmnd *ipr_cmd) +{ + struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg; + + ENTER; + ioa_cfg->in_reset_reload = 0; + ioa_cfg->reset_retries = 0; + list_add_tail(&ipr_cmd->queue, &ioa_cfg->free_q); + wake_up_all(&ioa_cfg->reset_wait_q); + + spin_unlock_irq(ioa_cfg->host->host_lock); + scsi_unblock_requests(ioa_cfg->host); + spin_lock_irq(ioa_cfg->host->host_lock); + LEAVE; + + return IPR_RC_JOB_RETURN; +} + +/** + * ipr_ioa_reset_done - IOA reset completion. + * @ipr_cmd: ipr command struct + * + * This function processes the completion of an adapter reset. + * It schedules any necessary mid-layer add/removes and + * wakes any reset sleepers. + * + * Return value: + * IPR_RC_JOB_RETURN + **/ +static int ipr_ioa_reset_done(struct ipr_cmnd *ipr_cmd) +{ + struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg; + struct ipr_resource_entry *res; + struct ipr_hostrcb *hostrcb, *temp; + int i = 0; + + ENTER; + ioa_cfg->in_reset_reload = 0; + ioa_cfg->allow_cmds = 1; + ioa_cfg->reset_cmd = NULL; + + list_for_each_entry(res, &ioa_cfg->used_res_q, queue) { + if (ioa_cfg->allow_ml_add_del && (res->add_to_ml || res->del_from_ml)) { + ipr_trace; + schedule_work(&ioa_cfg->work_q); + break; + } + } + + list_for_each_entry_safe(hostrcb, temp, &ioa_cfg->hostrcb_free_q, queue) { + list_del(&hostrcb->queue); + if (i++ < IPR_NUM_LOG_HCAMS) + ipr_send_hcam(ioa_cfg, IPR_HCAM_CDB_OP_CODE_LOG_DATA, hostrcb); + else + ipr_send_hcam(ioa_cfg, IPR_HCAM_CDB_OP_CODE_CONFIG_CHANGE, hostrcb); + } + + dev_info(&ioa_cfg->pdev->dev, "IOA initialized.\n"); + + ioa_cfg->reset_retries = 0; + list_add_tail(&ipr_cmd->queue, &ioa_cfg->free_q); + wake_up_all(&ioa_cfg->reset_wait_q); + + spin_unlock_irq(ioa_cfg->host->host_lock); + scsi_unblock_requests(ioa_cfg->host); + spin_lock_irq(ioa_cfg->host->host_lock); + + if (!ioa_cfg->allow_cmds) + scsi_block_requests(ioa_cfg->host); + + LEAVE; + return IPR_RC_JOB_RETURN; +} + +/** + * ipr_set_sup_dev_dflt - Initialize a Set Supported Device buffer + * @supported_dev: supported device struct + * @vpids: vendor product id struct + * + * Return value: + * none + **/ +static void ipr_set_sup_dev_dflt(struct ipr_supported_device *supported_dev, + struct ipr_std_inq_vpids *vpids) +{ + memset(supported_dev, 0, sizeof(struct ipr_supported_device)); + memcpy(&supported_dev->vpids, vpids, sizeof(struct ipr_std_inq_vpids)); + supported_dev->num_records = 1; + supported_dev->data_length = + cpu_to_be16(sizeof(struct ipr_supported_device)); + supported_dev->reserved = 0; +} + +/** + * ipr_set_supported_devs - Send Set Supported Devices for a device + * @ipr_cmd: ipr command struct + * + * This function send a Set Supported Devices to the adapter + * + * Return value: + * IPR_RC_JOB_CONTINUE / IPR_RC_JOB_RETURN + **/ +static int ipr_set_supported_devs(struct ipr_cmnd *ipr_cmd) +{ + struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg; + struct ipr_supported_device *supp_dev = &ioa_cfg->vpd_cbs->supp_dev; + struct ipr_ioadl_desc *ioadl = ipr_cmd->ioadl; + struct ipr_ioarcb *ioarcb = &ipr_cmd->ioarcb; + struct ipr_resource_entry *res = ipr_cmd->u.res; + + ipr_cmd->job_step = ipr_ioa_reset_done; + + list_for_each_entry_continue(res, &ioa_cfg->used_res_q, queue) { + if (!ipr_is_af_dasd_device(res)) + continue; + + ipr_cmd->u.res = res; + ipr_set_sup_dev_dflt(supp_dev, &res->cfgte.std_inq_data.vpids); + + ioarcb->res_handle = cpu_to_be32(IPR_IOA_RES_HANDLE); + ioarcb->cmd_pkt.flags_hi |= IPR_FLAGS_HI_WRITE_NOT_READ; + ioarcb->cmd_pkt.request_type = IPR_RQTYPE_IOACMD; + + ioarcb->cmd_pkt.cdb[0] = IPR_SET_SUPPORTED_DEVICES; + ioarcb->cmd_pkt.cdb[7] = (sizeof(struct ipr_supported_device) >> 8) & 0xff; + ioarcb->cmd_pkt.cdb[8] = sizeof(struct ipr_supported_device) & 0xff; + + ioadl->flags_and_data_len = cpu_to_be32(IPR_IOADL_FLAGS_WRITE_LAST | + sizeof(struct ipr_supported_device)); + ioadl->address = cpu_to_be32(ioa_cfg->vpd_cbs_dma + + offsetof(struct ipr_misc_cbs, supp_dev)); + ioarcb->write_ioadl_len = cpu_to_be32(sizeof(struct ipr_ioadl_desc)); + ioarcb->write_data_transfer_length = + cpu_to_be32(sizeof(struct ipr_supported_device)); + + ipr_do_req(ipr_cmd, ipr_reset_ioa_job, ipr_timeout, + IPR_SET_SUP_DEVICE_TIMEOUT); + + ipr_cmd->job_step = ipr_set_supported_devs; + return IPR_RC_JOB_RETURN; + } + + return IPR_RC_JOB_CONTINUE; +} + +/** + * ipr_get_mode_page - Locate specified mode page + * @mode_pages: mode page buffer + * @page_code: page code to find + * @len: minimum required length for mode page + * + * Return value: + * pointer to mode page / NULL on failure + **/ +static void *ipr_get_mode_page(struct ipr_mode_pages *mode_pages, + u32 page_code, u32 len) +{ + struct ipr_mode_page_hdr *mode_hdr; + u32 page_length; + u32 length; + + if (!mode_pages || (mode_pages->hdr.length == 0)) + return NULL; + + length = (mode_pages->hdr.length + 1) - 4 - mode_pages->hdr.block_desc_len; + mode_hdr = (struct ipr_mode_page_hdr *) + (mode_pages->data + mode_pages->hdr.block_desc_len); + + while (length) { + if (IPR_GET_MODE_PAGE_CODE(mode_hdr) == page_code) { + if (mode_hdr->page_length >= (len - sizeof(struct ipr_mode_page_hdr))) + return mode_hdr; + break; + } else { + page_length = (sizeof(struct ipr_mode_page_hdr) + + mode_hdr->page_length); + length -= page_length; + mode_hdr = (struct ipr_mode_page_hdr *) + ((unsigned long)mode_hdr + page_length); + } + } + return NULL; +} + +/** + * ipr_check_term_power - Check for term power errors + * @ioa_cfg: ioa config struct + * @mode_pages: IOAFP mode pages buffer + * + * Check the IOAFP's mode page 28 for term power errors + * + * Return value: + * nothing + **/ +static void ipr_check_term_power(struct ipr_ioa_cfg *ioa_cfg, + struct ipr_mode_pages *mode_pages) +{ + int i; + int entry_length; + struct ipr_dev_bus_entry *bus; + struct ipr_mode_page28 *mode_page; + + mode_page = ipr_get_mode_page(mode_pages, 0x28, + sizeof(struct ipr_mode_page28)); + + entry_length = mode_page->entry_length; + + bus = mode_page->bus; + + for (i = 0; i < mode_page->num_entries; i++) { + if (bus->flags & IPR_SCSI_ATTR_NO_TERM_PWR) { + dev_err(&ioa_cfg->pdev->dev, + "Term power is absent on scsi bus %d\n", + bus->res_addr.bus); + } + + bus = (struct ipr_dev_bus_entry *)((char *)bus + entry_length); + } +} + +/** + * ipr_scsi_bus_speed_limit - Limit the SCSI speed based on SES table + * @ioa_cfg: ioa config struct + * + * Looks through the config table checking for SES devices. If + * the SES device is in the SES table indicating a maximum SCSI + * bus speed, the speed is limited for the bus. + * + * Return value: + * none + **/ +static void ipr_scsi_bus_speed_limit(struct ipr_ioa_cfg *ioa_cfg) +{ + u32 max_xfer_rate; + int i; + + for (i = 0; i < IPR_MAX_NUM_BUSES; i++) { + max_xfer_rate = ipr_get_max_scsi_speed(ioa_cfg, i, + ioa_cfg->bus_attr[i].bus_width); + + if (max_xfer_rate < ioa_cfg->bus_attr[i].max_xfer_rate) + ioa_cfg->bus_attr[i].max_xfer_rate = max_xfer_rate; + } +} + +/** + * ipr_modify_ioafp_mode_page_28 - Modify IOAFP Mode Page 28 + * @ioa_cfg: ioa config struct + * @mode_pages: mode page 28 buffer + * + * Updates mode page 28 based on driver configuration + * + * Return value: + * none + **/ +static void ipr_modify_ioafp_mode_page_28(struct ipr_ioa_cfg *ioa_cfg, + struct ipr_mode_pages *mode_pages) +{ + int i, entry_length; + struct ipr_dev_bus_entry *bus; + struct ipr_bus_attributes *bus_attr; + struct ipr_mode_page28 *mode_page; + + mode_page = ipr_get_mode_page(mode_pages, 0x28, + sizeof(struct ipr_mode_page28)); + + entry_length = mode_page->entry_length; + + /* Loop for each device bus entry */ + for (i = 0, bus = mode_page->bus; + i < mode_page->num_entries; + i++, bus = (struct ipr_dev_bus_entry *)((u8 *)bus + entry_length)) { + if (bus->res_addr.bus > IPR_MAX_NUM_BUSES) { + dev_err(&ioa_cfg->pdev->dev, + "Invalid resource address reported: 0x%08X\n", + IPR_GET_PHYS_LOC(bus->res_addr)); + continue; + } + + bus_attr = &ioa_cfg->bus_attr[i]; + bus->extended_reset_delay = IPR_EXTENDED_RESET_DELAY; + bus->bus_width = bus_attr->bus_width; + bus->max_xfer_rate = cpu_to_be32(bus_attr->max_xfer_rate); + bus->flags &= ~IPR_SCSI_ATTR_QAS_MASK; + if (bus_attr->qas_enabled) + bus->flags |= IPR_SCSI_ATTR_ENABLE_QAS; + else + bus->flags |= IPR_SCSI_ATTR_DISABLE_QAS; + } +} + +/** + * ipr_build_mode_select - Build a mode select command + * @ipr_cmd: ipr command struct + * @res_handle: resource handle to send command to + * @parm: Byte 2 of Mode Sense command + * @dma_addr: DMA buffer address + * @xfer_len: data transfer length + * + * Return value: + * none + **/ +static void ipr_build_mode_select(struct ipr_cmnd *ipr_cmd, + u32 res_handle, u8 parm, u32 dma_addr, + u8 xfer_len) +{ + struct ipr_ioadl_desc *ioadl = ipr_cmd->ioadl; + struct ipr_ioarcb *ioarcb = &ipr_cmd->ioarcb; + + ioarcb->res_handle = res_handle; + ioarcb->cmd_pkt.request_type = IPR_RQTYPE_SCSICDB; + ioarcb->cmd_pkt.flags_hi |= IPR_FLAGS_HI_WRITE_NOT_READ; + ioarcb->cmd_pkt.cdb[0] = MODE_SELECT; + ioarcb->cmd_pkt.cdb[1] = parm; + ioarcb->cmd_pkt.cdb[4] = xfer_len; + + ioadl->flags_and_data_len = + cpu_to_be32(IPR_IOADL_FLAGS_WRITE_LAST | xfer_len); + ioadl->address = cpu_to_be32(dma_addr); + ioarcb->write_ioadl_len = cpu_to_be32(sizeof(struct ipr_ioadl_desc)); + ioarcb->write_data_transfer_length = cpu_to_be32(xfer_len); +} + +/** + * ipr_ioafp_mode_select_page28 - Issue Mode Select Page 28 to IOA + * @ipr_cmd: ipr command struct + * + * This function sets up the SCSI bus attributes and sends + * a Mode Select for Page 28 to activate them. + * + * Return value: + * IPR_RC_JOB_RETURN + **/ +static int ipr_ioafp_mode_select_page28(struct ipr_cmnd *ipr_cmd) +{ + struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg; + struct ipr_mode_pages *mode_pages = &ioa_cfg->vpd_cbs->mode_pages; + int length; + + ENTER; + if (ioa_cfg->saved_mode_pages) { + memcpy(mode_pages, ioa_cfg->saved_mode_pages, + ioa_cfg->saved_mode_page_len); + length = ioa_cfg->saved_mode_page_len; + } else { + ipr_scsi_bus_speed_limit(ioa_cfg); + ipr_check_term_power(ioa_cfg, mode_pages); + ipr_modify_ioafp_mode_page_28(ioa_cfg, mode_pages); + length = mode_pages->hdr.length + 1; + mode_pages->hdr.length = 0; + } + + ipr_build_mode_select(ipr_cmd, cpu_to_be32(IPR_IOA_RES_HANDLE), 0x11, + ioa_cfg->vpd_cbs_dma + offsetof(struct ipr_misc_cbs, mode_pages), + length); + + ipr_cmd->job_step = ipr_set_supported_devs; + ipr_cmd->u.res = list_entry(ioa_cfg->used_res_q.next, + struct ipr_resource_entry, queue); + + ipr_do_req(ipr_cmd, ipr_reset_ioa_job, ipr_timeout, IPR_INTERNAL_TIMEOUT); + + LEAVE; + return IPR_RC_JOB_RETURN; +} + +/** + * ipr_build_mode_sense - Builds a mode sense command + * @ipr_cmd: ipr command struct + * @res: resource entry struct + * @parm: Byte 2 of mode sense command + * @dma_addr: DMA address of mode sense buffer + * @xfer_len: Size of DMA buffer + * + * Return value: + * none + **/ +static void ipr_build_mode_sense(struct ipr_cmnd *ipr_cmd, + u32 res_handle, + u8 parm, u32 dma_addr, u8 xfer_len) +{ + struct ipr_ioadl_desc *ioadl = ipr_cmd->ioadl; + struct ipr_ioarcb *ioarcb = &ipr_cmd->ioarcb; + + ioarcb->res_handle = res_handle; + ioarcb->cmd_pkt.cdb[0] = MODE_SENSE; + ioarcb->cmd_pkt.cdb[2] = parm; + ioarcb->cmd_pkt.cdb[4] = xfer_len; + ioarcb->cmd_pkt.request_type = IPR_RQTYPE_SCSICDB; + + ioadl->flags_and_data_len = + cpu_to_be32(IPR_IOADL_FLAGS_READ_LAST | xfer_len); + ioadl->address = cpu_to_be32(dma_addr); + ioarcb->read_ioadl_len = cpu_to_be32(sizeof(struct ipr_ioadl_desc)); + ioarcb->read_data_transfer_length = cpu_to_be32(xfer_len); +} + +/** + * ipr_ioafp_mode_sense_page28 - Issue Mode Sense Page 28 to IOA + * @ipr_cmd: ipr command struct + * + * This function send a Page 28 mode sense to the IOA to + * retrieve SCSI bus attributes. + * + * Return value: + * IPR_RC_JOB_RETURN + **/ +static int ipr_ioafp_mode_sense_page28(struct ipr_cmnd *ipr_cmd) +{ + struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg; + + ENTER; + ipr_build_mode_sense(ipr_cmd, cpu_to_be32(IPR_IOA_RES_HANDLE), + 0x28, ioa_cfg->vpd_cbs_dma + + offsetof(struct ipr_misc_cbs, mode_pages), + sizeof(struct ipr_mode_pages)); + + ipr_cmd->job_step = ipr_ioafp_mode_select_page28; + + ipr_do_req(ipr_cmd, ipr_reset_ioa_job, ipr_timeout, IPR_INTERNAL_TIMEOUT); + + LEAVE; + return IPR_RC_JOB_RETURN; +} + +/** + * ipr_init_res_table - Initialize the resource table + * @ipr_cmd: ipr command struct + * + * This function looks through the existing resource table, comparing + * it with the config table. This function will take care of old/new + * devices and schedule adding/removing them from the mid-layer + * as appropriate. + * + * Return value: + * IPR_RC_JOB_CONTINUE + **/ +static int ipr_init_res_table(struct ipr_cmnd *ipr_cmd) +{ + struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg; + struct ipr_resource_entry *res, *temp; + struct ipr_config_table_entry *cfgte; + int found, i; + LIST_HEAD(old_res); + + ENTER; + if (ioa_cfg->cfg_table->hdr.flags & IPR_UCODE_DOWNLOAD_REQ) + dev_err(&ioa_cfg->pdev->dev, "Microcode download required\n"); + + list_for_each_entry_safe(res, temp, &ioa_cfg->used_res_q, queue) + list_move_tail(&res->queue, &old_res); + + for (i = 0; i < ioa_cfg->cfg_table->hdr.num_entries; i++) { + cfgte = &ioa_cfg->cfg_table->dev[i]; + found = 0; + + list_for_each_entry_safe(res, temp, &old_res, queue) { + if (!memcmp(&res->cfgte.res_addr, + &cfgte->res_addr, sizeof(cfgte->res_addr))) { + list_move_tail(&res->queue, &ioa_cfg->used_res_q); + found = 1; + break; + } + } + + if (!found) { + if (list_empty(&ioa_cfg->free_res_q)) { + dev_err(&ioa_cfg->pdev->dev, "Too many devices attached\n"); + break; + } + + found = 1; + res = list_entry(ioa_cfg->free_res_q.next, + struct ipr_resource_entry, queue); + list_move_tail(&res->queue, &ioa_cfg->used_res_q); + ipr_init_res_entry(res); + res->add_to_ml = 1; + } + + if (found) + memcpy(&res->cfgte, cfgte, sizeof(struct ipr_config_table_entry)); + } + + list_for_each_entry_safe(res, temp, &old_res, queue) { + if (res->sdev) { + res->del_from_ml = 1; + list_move_tail(&res->queue, &ioa_cfg->used_res_q); + } else { + list_move_tail(&res->queue, &ioa_cfg->free_res_q); + } + } + + ipr_cmd->job_step = ipr_ioafp_mode_sense_page28; + + LEAVE; + return IPR_RC_JOB_CONTINUE; +} + +/** + * ipr_ioafp_query_ioa_cfg - Send a Query IOA Config to the adapter. + * @ipr_cmd: ipr command struct + * + * This function sends a Query IOA Configuration command + * to the adapter to retrieve the IOA configuration table. + * + * Return value: + * IPR_RC_JOB_RETURN + **/ +static int ipr_ioafp_query_ioa_cfg(struct ipr_cmnd *ipr_cmd) +{ + struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg; + struct ipr_ioarcb *ioarcb = &ipr_cmd->ioarcb; + struct ipr_ioadl_desc *ioadl = ipr_cmd->ioadl; + struct ipr_inquiry_page3 *ucode_vpd = &ioa_cfg->vpd_cbs->page3_data; + + ENTER; + dev_info(&ioa_cfg->pdev->dev, "Adapter firmware version: %02X%02X%02X%02X\n", + ucode_vpd->major_release, ucode_vpd->card_type, + ucode_vpd->minor_release[0], ucode_vpd->minor_release[1]); + ioarcb->cmd_pkt.request_type = IPR_RQTYPE_IOACMD; + ioarcb->res_handle = cpu_to_be32(IPR_IOA_RES_HANDLE); + + ioarcb->cmd_pkt.cdb[0] = IPR_QUERY_IOA_CONFIG; + ioarcb->cmd_pkt.cdb[7] = (sizeof(struct ipr_config_table) >> 8) & 0xff; + ioarcb->cmd_pkt.cdb[8] = sizeof(struct ipr_config_table) & 0xff; + + ioarcb->read_ioadl_len = cpu_to_be32(sizeof(struct ipr_ioadl_desc)); + ioarcb->read_data_transfer_length = + cpu_to_be32(sizeof(struct ipr_config_table)); + + ioadl->address = cpu_to_be32(ioa_cfg->cfg_table_dma); + ioadl->flags_and_data_len = + cpu_to_be32(IPR_IOADL_FLAGS_READ_LAST | sizeof(struct ipr_config_table)); + + ipr_cmd->job_step = ipr_init_res_table; + + ipr_do_req(ipr_cmd, ipr_reset_ioa_job, ipr_timeout, IPR_INTERNAL_TIMEOUT); + + LEAVE; + return IPR_RC_JOB_RETURN; +} + +/** + * ipr_ioafp_inquiry - Send an Inquiry to the adapter. + * @ipr_cmd: ipr command struct + * + * This utility function sends an inquiry to the adapter. + * + * Return value: + * none + **/ +static void ipr_ioafp_inquiry(struct ipr_cmnd *ipr_cmd, u8 flags, u8 page, + u32 dma_addr, u8 xfer_len) +{ + struct ipr_ioarcb *ioarcb = &ipr_cmd->ioarcb; + struct ipr_ioadl_desc *ioadl = ipr_cmd->ioadl; + + ENTER; + ioarcb->cmd_pkt.request_type = IPR_RQTYPE_SCSICDB; + ioarcb->res_handle = cpu_to_be32(IPR_IOA_RES_HANDLE); + + ioarcb->cmd_pkt.cdb[0] = INQUIRY; + ioarcb->cmd_pkt.cdb[1] = flags; + ioarcb->cmd_pkt.cdb[2] = page; + ioarcb->cmd_pkt.cdb[4] = xfer_len; + + ioarcb->read_ioadl_len = cpu_to_be32(sizeof(struct ipr_ioadl_desc)); + ioarcb->read_data_transfer_length = cpu_to_be32(xfer_len); + + ioadl->address = cpu_to_be32(dma_addr); + ioadl->flags_and_data_len = + cpu_to_be32(IPR_IOADL_FLAGS_READ_LAST | xfer_len); + + ipr_do_req(ipr_cmd, ipr_reset_ioa_job, ipr_timeout, IPR_INTERNAL_TIMEOUT); + LEAVE; +} + +/** + * ipr_ioafp_page3_inquiry - Send a Page 3 Inquiry to the adapter. + * @ipr_cmd: ipr command struct + * + * This function sends a Page 3 inquiry to the adapter + * to retrieve software VPD information. + * + * Return value: + * IPR_RC_JOB_CONTINUE / IPR_RC_JOB_RETURN + **/ +static int ipr_ioafp_page3_inquiry(struct ipr_cmnd *ipr_cmd) +{ + struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg; + char type[5]; + + ENTER; + + /* Grab the type out of the VPD and store it away */ + memcpy(type, ioa_cfg->vpd_cbs->ioa_vpd.std_inq_data.vpids.product_id, 4); + type[4] = '\0'; + ioa_cfg->type = simple_strtoul((char *)type, NULL, 16); + + ipr_cmd->job_step = ipr_ioafp_query_ioa_cfg; + + ipr_ioafp_inquiry(ipr_cmd, 1, 3, + ioa_cfg->vpd_cbs_dma + offsetof(struct ipr_misc_cbs, page3_data), + sizeof(struct ipr_inquiry_page3)); + + LEAVE; + return IPR_RC_JOB_RETURN; +} + +/** + * ipr_ioafp_std_inquiry - Send a Standard Inquiry to the adapter. + * @ipr_cmd: ipr command struct + * + * This function sends a standard inquiry to the adapter. + * + * Return value: + * IPR_RC_JOB_RETURN + **/ +static int ipr_ioafp_std_inquiry(struct ipr_cmnd *ipr_cmd) +{ + struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg; + + ENTER; + ipr_cmd->job_step = ipr_ioafp_page3_inquiry; + + ipr_ioafp_inquiry(ipr_cmd, 0, 0, + ioa_cfg->vpd_cbs_dma + offsetof(struct ipr_misc_cbs, ioa_vpd), + sizeof(struct ipr_ioa_vpd)); + + LEAVE; + return IPR_RC_JOB_RETURN; +} + +/** + * ipr_ioafp_indentify_hrrq - Send Identify Host RRQ. + * @ipr_cmd: ipr command struct + * + * This function send an Identify Host Request Response Queue + * command to establish the HRRQ with the adapter. + * + * Return value: + * IPR_RC_JOB_RETURN + **/ +static int ipr_ioafp_indentify_hrrq(struct ipr_cmnd *ipr_cmd) +{ + struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg; + struct ipr_ioarcb *ioarcb = &ipr_cmd->ioarcb; + + ENTER; + dev_info(&ioa_cfg->pdev->dev, "Starting IOA initialization sequence.\n"); + + ioarcb->cmd_pkt.cdb[0] = IPR_ID_HOST_RR_Q; + ioarcb->res_handle = cpu_to_be32(IPR_IOA_RES_HANDLE); + + ioarcb->cmd_pkt.request_type = IPR_RQTYPE_IOACMD; + ioarcb->cmd_pkt.cdb[2] = + ((u32) ioa_cfg->host_rrq_dma >> 24) & 0xff; + ioarcb->cmd_pkt.cdb[3] = + ((u32) ioa_cfg->host_rrq_dma >> 16) & 0xff; + ioarcb->cmd_pkt.cdb[4] = + ((u32) ioa_cfg->host_rrq_dma >> 8) & 0xff; + ioarcb->cmd_pkt.cdb[5] = + ((u32) ioa_cfg->host_rrq_dma) & 0xff; + ioarcb->cmd_pkt.cdb[7] = + ((sizeof(u32) * IPR_NUM_CMD_BLKS) >> 8) & 0xff; + ioarcb->cmd_pkt.cdb[8] = + (sizeof(u32) * IPR_NUM_CMD_BLKS) & 0xff; + + ipr_cmd->job_step = ipr_ioafp_std_inquiry; + + ipr_do_req(ipr_cmd, ipr_reset_ioa_job, ipr_timeout, IPR_INTERNAL_TIMEOUT); + + LEAVE; + return IPR_RC_JOB_RETURN; +} + +/** + * ipr_reset_timer_done - Adapter reset timer function + * @ipr_cmd: ipr command struct + * + * Description: This function is used in adapter reset processing + * for timing events. If the reset_cmd pointer in the IOA + * config struct is not this adapter's we are doing nested + * resets and fail_all_ops will take care of freeing the + * command block. + * + * Return value: + * none + **/ +static void ipr_reset_timer_done(struct ipr_cmnd *ipr_cmd) +{ + struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg; + unsigned long lock_flags = 0; + + spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); + + if (ioa_cfg->reset_cmd == ipr_cmd) { + list_del(&ipr_cmd->queue); + ipr_cmd->done(ipr_cmd); + } + + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); +} + +/** + * ipr_reset_start_timer - Start a timer for adapter reset job + * @ipr_cmd: ipr command struct + * @timeout: timeout value + * + * Description: This function is used in adapter reset processing + * for timing events. If the reset_cmd pointer in the IOA + * config struct is not this adapter's we are doing nested + * resets and fail_all_ops will take care of freeing the + * command block. + * + * Return value: + * none + **/ +static void ipr_reset_start_timer(struct ipr_cmnd *ipr_cmd, + unsigned long timeout) +{ + list_add_tail(&ipr_cmd->queue, &ipr_cmd->ioa_cfg->pending_q); + ipr_cmd->done = ipr_reset_ioa_job; + + ipr_cmd->timer.data = (unsigned long) ipr_cmd; + ipr_cmd->timer.expires = jiffies + timeout; + ipr_cmd->timer.function = (void (*)(unsigned long))ipr_reset_timer_done; + add_timer(&ipr_cmd->timer); +} + +/** + * ipr_init_ioa_mem - Initialize ioa_cfg control block + * @ioa_cfg: ioa cfg struct + * + * Return value: + * nothing + **/ +static void ipr_init_ioa_mem(struct ipr_ioa_cfg *ioa_cfg) +{ + memset(ioa_cfg->host_rrq, 0, sizeof(u32) * IPR_NUM_CMD_BLKS); + + /* Initialize Host RRQ pointers */ + ioa_cfg->hrrq_start = ioa_cfg->host_rrq; + ioa_cfg->hrrq_end = &ioa_cfg->host_rrq[IPR_NUM_CMD_BLKS - 1]; + ioa_cfg->hrrq_curr = ioa_cfg->hrrq_start; + ioa_cfg->toggle_bit = 1; + + /* Zero out config table */ + memset(ioa_cfg->cfg_table, 0, sizeof(struct ipr_config_table)); +} + +/** + * ipr_reset_enable_ioa - Enable the IOA following a reset. + * @ipr_cmd: ipr command struct + * + * This function reinitializes some control blocks and + * enables destructive diagnostics on the adapter. + * + * Return value: + * IPR_RC_JOB_RETURN + **/ +static int ipr_reset_enable_ioa(struct ipr_cmnd *ipr_cmd) +{ + struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg; + volatile u32 int_reg; + + ENTER; + ipr_cmd->job_step = ipr_ioafp_indentify_hrrq; + ipr_init_ioa_mem(ioa_cfg); + + ioa_cfg->allow_interrupts = 1; + int_reg = readl(ioa_cfg->regs.sense_interrupt_reg); + + if (int_reg & IPR_PCII_IOA_TRANS_TO_OPER) { + writel((IPR_PCII_ERROR_INTERRUPTS | IPR_PCII_HRRQ_UPDATED), + ioa_cfg->regs.clr_interrupt_mask_reg); + int_reg = readl(ioa_cfg->regs.sense_interrupt_mask_reg); + return IPR_RC_JOB_CONTINUE; + } + + /* Enable destructive diagnostics on IOA */ + writel(IPR_DOORBELL, ioa_cfg->regs.set_uproc_interrupt_reg); + + writel(IPR_PCII_OPER_INTERRUPTS, ioa_cfg->regs.clr_interrupt_mask_reg); + int_reg = readl(ioa_cfg->regs.sense_interrupt_mask_reg); + + dev_info(&ioa_cfg->pdev->dev, "Initializing IOA.\n"); + + ipr_cmd->timer.data = (unsigned long) ipr_cmd; + ipr_cmd->timer.expires = jiffies + IPR_OPERATIONAL_TIMEOUT; + ipr_cmd->timer.function = (void (*)(unsigned long))ipr_timeout; + add_timer(&ipr_cmd->timer); + list_add_tail(&ipr_cmd->queue, &ioa_cfg->pending_q); + + LEAVE; + return IPR_RC_JOB_RETURN; +} + +/** + * ipr_reset_wait_for_dump - Wait for a dump to timeout. + * @ipr_cmd: ipr command struct + * + * This function is invoked when an adapter dump has run out + * of processing time. + * + * Return value: + * IPR_RC_JOB_CONTINUE + **/ +static int ipr_reset_wait_for_dump(struct ipr_cmnd *ipr_cmd) +{ + struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg; + + if (ioa_cfg->sdt_state == GET_DUMP) + ioa_cfg->sdt_state = ABORT_DUMP; + + ipr_cmd->job_step = ipr_reset_alert; + + return IPR_RC_JOB_CONTINUE; +} + +/** + * ipr_unit_check_no_data - Log a unit check/no data error log + * @ioa_cfg: ioa config struct + * + * Logs an error indicating the adapter unit checked, but for some + * reason, we were unable to fetch the unit check buffer. + * + * Return value: + * nothing + **/ +static void ipr_unit_check_no_data(struct ipr_ioa_cfg *ioa_cfg) +{ + ioa_cfg->errors_logged++; + dev_err(&ioa_cfg->pdev->dev, "IOA unit check with no data\n"); +} + +/** + * ipr_get_unit_check_buffer - Get the unit check buffer from the IOA + * @ioa_cfg: ioa config struct + * + * Fetches the unit check buffer from the adapter by clocking the data + * through the mailbox register. + * + * Return value: + * nothing + **/ +static void ipr_get_unit_check_buffer(struct ipr_ioa_cfg *ioa_cfg) +{ + unsigned long mailbox; + struct ipr_hostrcb *hostrcb; + struct ipr_uc_sdt sdt; + int rc, length; + + mailbox = readl(ioa_cfg->ioa_mailbox); + + if (!ipr_sdt_is_fmt2(mailbox)) { + ipr_unit_check_no_data(ioa_cfg); + return; + } + + memset(&sdt, 0, sizeof(struct ipr_uc_sdt)); + rc = ipr_get_ldump_data_section(ioa_cfg, mailbox, (u32 *) &sdt, + (sizeof(struct ipr_uc_sdt)) / sizeof(u32)); + + if (rc || (be32_to_cpu(sdt.hdr.state) != IPR_FMT2_SDT_READY_TO_USE) || + !(sdt.entry[0].flags & IPR_SDT_VALID_ENTRY)) { + ipr_unit_check_no_data(ioa_cfg); + return; + } + + /* Find length of the first sdt entry (UC buffer) */ + length = (be32_to_cpu(sdt.entry[0].end_offset) - + be32_to_cpu(sdt.entry[0].bar_str_offset)) & IPR_FMT2_MBX_ADDR_MASK; + + hostrcb = list_entry(ioa_cfg->hostrcb_free_q.next, + struct ipr_hostrcb, queue); + list_del(&hostrcb->queue); + memset(&hostrcb->hcam, 0, sizeof(hostrcb->hcam)); + + rc = ipr_get_ldump_data_section(ioa_cfg, + be32_to_cpu(sdt.entry[0].bar_str_offset), + (u32 *)&hostrcb->hcam, + min(length, (int)sizeof(hostrcb->hcam)) / sizeof(u32)); + + if (!rc) + ipr_handle_log_data(ioa_cfg, hostrcb); + else + ipr_unit_check_no_data(ioa_cfg); + + list_add_tail(&hostrcb->queue, &ioa_cfg->hostrcb_free_q); +} + +/** + * ipr_reset_restore_cfg_space - Restore PCI config space. + * @ipr_cmd: ipr command struct + * + * Description: This function restores the saved PCI config space of + * the adapter, fails all outstanding ops back to the callers, and + * fetches the dump/unit check if applicable to this reset. + * + * Return value: + * IPR_RC_JOB_CONTINUE / IPR_RC_JOB_RETURN + **/ +static int ipr_reset_restore_cfg_space(struct ipr_cmnd *ipr_cmd) +{ + struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg; + int rc; + + ENTER; + rc = pci_restore_state(ioa_cfg->pdev, ioa_cfg->pci_cfg_buf); + + if (rc != PCIBIOS_SUCCESSFUL) { + ipr_cmd->ioasa.ioasc = cpu_to_be32(IPR_IOASC_PCI_ACCESS_ERROR); + return IPR_RC_JOB_CONTINUE; + } + + if (ipr_set_pcix_cmd_reg(ioa_cfg)) { + ipr_cmd->ioasa.ioasc = cpu_to_be32(IPR_IOASC_PCI_ACCESS_ERROR); + return IPR_RC_JOB_CONTINUE; + } + + ipr_fail_all_ops(ioa_cfg); + + if (ioa_cfg->ioa_unit_checked) { + ioa_cfg->ioa_unit_checked = 0; + ipr_get_unit_check_buffer(ioa_cfg); + ipr_cmd->job_step = ipr_reset_alert; + ipr_reset_start_timer(ipr_cmd, 0); + return IPR_RC_JOB_RETURN; + } + + if (ioa_cfg->in_ioa_bringdown) { + ipr_cmd->job_step = ipr_ioa_bringdown_done; + } else { + ipr_cmd->job_step = ipr_reset_enable_ioa; + + if (GET_DUMP == ioa_cfg->sdt_state) { + ipr_reset_start_timer(ipr_cmd, IPR_DUMP_TIMEOUT); + ipr_cmd->job_step = ipr_reset_wait_for_dump; + schedule_work(&ioa_cfg->work_q); + return IPR_RC_JOB_RETURN; + } + } + + ENTER; + return IPR_RC_JOB_CONTINUE; +} + +/** + * ipr_reset_start_bist - Run BIST on the adapter. + * @ipr_cmd: ipr command struct + * + * Description: This function runs BIST on the adapter, then delays 2 seconds. + * + * Return value: + * IPR_RC_JOB_CONTINUE / IPR_RC_JOB_RETURN + **/ +static int ipr_reset_start_bist(struct ipr_cmnd *ipr_cmd) +{ + struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg; + int rc; + + ENTER; + rc = pci_write_config_byte(ioa_cfg->pdev, PCI_BIST, PCI_BIST_START); + + if (rc != PCIBIOS_SUCCESSFUL) { + ipr_cmd->ioasa.ioasc = cpu_to_be32(IPR_IOASC_PCI_ACCESS_ERROR); + rc = IPR_RC_JOB_CONTINUE; + } else { + ipr_cmd->job_step = ipr_reset_restore_cfg_space; + ipr_reset_start_timer(ipr_cmd, IPR_WAIT_FOR_BIST_TIMEOUT); + rc = IPR_RC_JOB_RETURN; + } + + LEAVE; + return rc; +} + +/** + * ipr_reset_allowed - Query whether or not IOA can be reset + * @ioa_cfg: ioa config struct + * + * Return value: + * 0 if reset not allowed / non-zero if reset is allowed + **/ +static int ipr_reset_allowed(struct ipr_ioa_cfg *ioa_cfg) +{ + volatile u32 temp_reg; + + temp_reg = readl(ioa_cfg->regs.sense_interrupt_reg); + return ((temp_reg & IPR_PCII_CRITICAL_OPERATION) == 0); +} + +/** + * ipr_reset_wait_to_start_bist - Wait for permission to reset IOA. + * @ipr_cmd: ipr command struct + * + * Description: This function waits for adapter permission to run BIST, + * then runs BIST. If the adapter does not give permission after a + * reasonable time, we will reset the adapter anyway. The impact of + * resetting the adapter without warning the adapter is the risk of + * losing the persistent error log on the adapter. If the adapter is + * reset while it is writing to the flash on the adapter, the flash + * segment will have bad ECC and be zeroed. + * + * Return value: + * IPR_RC_JOB_CONTINUE / IPR_RC_JOB_RETURN + **/ +static int ipr_reset_wait_to_start_bist(struct ipr_cmnd *ipr_cmd) +{ + struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg; + int rc = IPR_RC_JOB_RETURN; + + if (!ipr_reset_allowed(ioa_cfg) && ipr_cmd->u.time_left) { + ipr_cmd->u.time_left -= IPR_CHECK_FOR_RESET_TIMEOUT; + ipr_reset_start_timer(ipr_cmd, IPR_CHECK_FOR_RESET_TIMEOUT); + } else { + ipr_cmd->job_step = ipr_reset_start_bist; + rc = IPR_RC_JOB_CONTINUE; + } + + return rc; +} + +/** + * ipr_reset_alert_part2 - Alert the adapter of a pending reset + * @ipr_cmd: ipr command struct + * + * Description: This function alerts the adapter that it will be reset. + * If memory space is not currently enabled, proceed directly + * to running BIST on the adapter. The timer must always be started + * so we guarantee we do not run BIST from ipr_isr. + * + * Return value: + * IPR_RC_JOB_RETURN + **/ +static int ipr_reset_alert(struct ipr_cmnd *ipr_cmd) +{ + struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg; + u16 cmd_reg; + int rc; + + ENTER; + rc = pci_read_config_word(ioa_cfg->pdev, PCI_COMMAND, &cmd_reg); + + if ((rc == PCIBIOS_SUCCESSFUL) && (cmd_reg & PCI_COMMAND_MEMORY)) { + ipr_mask_and_clear_interrupts(ioa_cfg, ~0); + writel(IPR_UPROCI_RESET_ALERT, ioa_cfg->regs.set_uproc_interrupt_reg); + ipr_cmd->job_step = ipr_reset_wait_to_start_bist; + } else { + ipr_cmd->job_step = ipr_reset_start_bist; + } + + ipr_cmd->u.time_left = IPR_WAIT_FOR_RESET_TIMEOUT; + ipr_reset_start_timer(ipr_cmd, IPR_CHECK_FOR_RESET_TIMEOUT); + + LEAVE; + return IPR_RC_JOB_RETURN; +} + +/** + * ipr_reset_ucode_download_done - Microcode download completion + * @ipr_cmd: ipr command struct + * + * Description: This function unmaps the microcode download buffer. + * + * Return value: + * IPR_RC_JOB_CONTINUE + **/ +static int ipr_reset_ucode_download_done(struct ipr_cmnd *ipr_cmd) +{ + struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg; + struct ipr_sglist *sglist = ioa_cfg->ucode_sglist; + + pci_unmap_sg(ioa_cfg->pdev, sglist->scatterlist, + sglist->num_sg, DMA_TO_DEVICE); + + ipr_cmd->job_step = ipr_reset_alert; + return IPR_RC_JOB_CONTINUE; +} + +/** + * ipr_reset_ucode_download - Download microcode to the adapter + * @ipr_cmd: ipr command struct + * + * Description: This function checks to see if it there is microcode + * to download to the adapter. If there is, a download is performed. + * + * Return value: + * IPR_RC_JOB_CONTINUE / IPR_RC_JOB_RETURN + **/ +static int ipr_reset_ucode_download(struct ipr_cmnd *ipr_cmd) +{ + struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg; + struct ipr_sglist *sglist = ioa_cfg->ucode_sglist; + + ENTER; + ipr_cmd->job_step = ipr_reset_alert; + + if (!sglist) + return IPR_RC_JOB_CONTINUE; + + ipr_cmd->ioarcb.res_handle = cpu_to_be32(IPR_IOA_RES_HANDLE); + ipr_cmd->ioarcb.cmd_pkt.request_type = IPR_RQTYPE_SCSICDB; + ipr_cmd->ioarcb.cmd_pkt.cdb[0] = WRITE_BUFFER; + ipr_cmd->ioarcb.cmd_pkt.cdb[1] = IPR_WR_BUF_DOWNLOAD_AND_SAVE; + ipr_cmd->ioarcb.cmd_pkt.cdb[6] = (sglist->buffer_len & 0xff0000) >> 16; + ipr_cmd->ioarcb.cmd_pkt.cdb[7] = (sglist->buffer_len & 0x00ff00) >> 8; + ipr_cmd->ioarcb.cmd_pkt.cdb[8] = sglist->buffer_len & 0x0000ff; + + if (ipr_map_ucode_buffer(ipr_cmd, sglist, sglist->buffer_len)) { + dev_err(&ioa_cfg->pdev->dev, + "Failed to map microcode download buffer\n"); + return IPR_RC_JOB_CONTINUE; + } + + ipr_cmd->job_step = ipr_reset_ucode_download_done; + + ipr_do_req(ipr_cmd, ipr_reset_ioa_job, ipr_timeout, + IPR_WRITE_BUFFER_TIMEOUT); + + LEAVE; + return IPR_RC_JOB_RETURN; +} + +/** + * ipr_reset_shutdown_ioa - Shutdown the adapter + * @ipr_cmd: ipr command struct + * + * Description: This function issues an adapter shutdown of the + * specified type to the specified adapter as part of the + * adapter reset job. + * + * Return value: + * IPR_RC_JOB_CONTINUE / IPR_RC_JOB_RETURN + **/ +static int ipr_reset_shutdown_ioa(struct ipr_cmnd *ipr_cmd) +{ + struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg; + enum ipr_shutdown_type shutdown_type = ipr_cmd->u.shutdown_type; + unsigned long timeout; + int rc = IPR_RC_JOB_CONTINUE; + + ENTER; + if (shutdown_type != IPR_SHUTDOWN_NONE && !ioa_cfg->ioa_is_dead) { + ipr_cmd->ioarcb.res_handle = cpu_to_be32(IPR_IOA_RES_HANDLE); + ipr_cmd->ioarcb.cmd_pkt.request_type = IPR_RQTYPE_IOACMD; + ipr_cmd->ioarcb.cmd_pkt.cdb[0] = IPR_IOA_SHUTDOWN; + ipr_cmd->ioarcb.cmd_pkt.cdb[1] = shutdown_type; + + if (shutdown_type == IPR_SHUTDOWN_ABBREV) + timeout = IPR_ABBREV_SHUTDOWN_TIMEOUT; + else if (shutdown_type == IPR_SHUTDOWN_PREPARE_FOR_NORMAL) + timeout = IPR_INTERNAL_TIMEOUT; + else + timeout = IPR_SHUTDOWN_TIMEOUT; + + ipr_do_req(ipr_cmd, ipr_reset_ioa_job, ipr_timeout, timeout); + + rc = IPR_RC_JOB_RETURN; + ipr_cmd->job_step = ipr_reset_ucode_download; + } else + ipr_cmd->job_step = ipr_reset_alert; + + LEAVE; + return rc; +} + +/** + * ipr_reset_ioa_job - Adapter reset job + * @ipr_cmd: ipr command struct + * + * Description: This function is the job router for the adapter reset job. + * + * Return value: + * none + **/ +static void ipr_reset_ioa_job(struct ipr_cmnd *ipr_cmd) +{ + u32 rc, ioasc; + unsigned long scratch = ipr_cmd->u.scratch; + struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg; + + do { + ioasc = be32_to_cpu(ipr_cmd->ioasa.ioasc); + + if (ioa_cfg->reset_cmd != ipr_cmd) { + /* + * We are doing nested adapter resets and this is + * not the current reset job. + */ + list_add_tail(&ipr_cmd->queue, &ioa_cfg->free_q); + return; + } + + if (IPR_IOASC_SENSE_KEY(ioasc)) { + dev_err(&ioa_cfg->pdev->dev, + "0x%02X failed with IOASC: 0x%08X\n", + ipr_cmd->ioarcb.cmd_pkt.cdb[0], ioasc); + + ipr_initiate_ioa_reset(ioa_cfg, IPR_SHUTDOWN_NONE); + list_add_tail(&ipr_cmd->queue, &ioa_cfg->free_q); + return; + } + + ipr_reinit_ipr_cmnd(ipr_cmd); + ipr_cmd->u.scratch = scratch; + rc = ipr_cmd->job_step(ipr_cmd); + } while(rc == IPR_RC_JOB_CONTINUE); +} + +/** + * _ipr_initiate_ioa_reset - Initiate an adapter reset + * @ioa_cfg: ioa config struct + * @job_step: first job step of reset job + * @shutdown_type: shutdown type + * + * Description: This function will initiate the reset of the given adapter + * starting at the selected job step. + * If the caller needs to wait on the completion of the reset, + * the caller must sleep on the reset_wait_q. + * + * Return value: + * none + **/ +static void _ipr_initiate_ioa_reset(struct ipr_ioa_cfg *ioa_cfg, + int (*job_step) (struct ipr_cmnd *), + enum ipr_shutdown_type shutdown_type) +{ + struct ipr_cmnd *ipr_cmd; + + ioa_cfg->in_reset_reload = 1; + ioa_cfg->allow_cmds = 0; + scsi_block_requests(ioa_cfg->host); + + ipr_cmd = ipr_get_free_ipr_cmnd(ioa_cfg); + ioa_cfg->reset_cmd = ipr_cmd; + ipr_cmd->job_step = job_step; + ipr_cmd->u.shutdown_type = shutdown_type; + + ipr_reset_ioa_job(ipr_cmd); +} + +/** + * ipr_initiate_ioa_reset - Initiate an adapter reset + * @ioa_cfg: ioa config struct + * @shutdown_type: shutdown type + * + * Description: This function will initiate the reset of the given adapter. + * If the caller needs to wait on the completion of the reset, + * the caller must sleep on the reset_wait_q. + * + * Return value: + * none + **/ +static void ipr_initiate_ioa_reset(struct ipr_ioa_cfg *ioa_cfg, + enum ipr_shutdown_type shutdown_type) +{ + if (ioa_cfg->ioa_is_dead) + return; + + if (ioa_cfg->in_reset_reload && ioa_cfg->sdt_state == GET_DUMP) + ioa_cfg->sdt_state = ABORT_DUMP; + + if (ioa_cfg->reset_retries++ > IPR_NUM_RESET_RELOAD_RETRIES) { + dev_err(&ioa_cfg->pdev->dev, + "IOA taken offline - error recovery failed\n"); + + ioa_cfg->reset_retries = 0; + ioa_cfg->ioa_is_dead = 1; + + if (ioa_cfg->in_ioa_bringdown) { + ioa_cfg->reset_cmd = NULL; + ioa_cfg->in_reset_reload = 0; + ipr_fail_all_ops(ioa_cfg); + wake_up_all(&ioa_cfg->reset_wait_q); + + spin_unlock_irq(ioa_cfg->host->host_lock); + scsi_unblock_requests(ioa_cfg->host); + spin_lock_irq(ioa_cfg->host->host_lock); + return; + } else { + ioa_cfg->in_ioa_bringdown = 1; + shutdown_type = IPR_SHUTDOWN_NONE; + } + } + + _ipr_initiate_ioa_reset(ioa_cfg, ipr_reset_shutdown_ioa, + shutdown_type); +} + +/** + * ipr_probe_ioa_part2 - Initializes IOAs found in ipr_probe_ioa(..) + * @ioa_cfg: ioa cfg struct + * + * Description: This is the second phase of adapter intialization + * This function takes care of initilizing the adapter to the point + * where it can accept new commands. + + * Return value: + * 0 on sucess / -EIO on failure + **/ +static int __devinit ipr_probe_ioa_part2(struct ipr_ioa_cfg *ioa_cfg) +{ + int rc = 0; + unsigned long host_lock_flags = 0; + + ENTER; + spin_lock_irqsave(ioa_cfg->host->host_lock, host_lock_flags); + dev_dbg(&ioa_cfg->pdev->dev, "ioa_cfg adx: 0x%p\n", ioa_cfg); + _ipr_initiate_ioa_reset(ioa_cfg, ipr_reset_enable_ioa, IPR_SHUTDOWN_NONE); + + spin_unlock_irqrestore(ioa_cfg->host->host_lock, host_lock_flags); + wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload); + spin_lock_irqsave(ioa_cfg->host->host_lock, host_lock_flags); + + if (ioa_cfg->ioa_is_dead) { + rc = -EIO; + } else if (ipr_invalid_adapter(ioa_cfg)) { + if (!ipr_testmode) + rc = -EIO; + + dev_err(&ioa_cfg->pdev->dev, + "Adapter not supported in this hardware configuration.\n"); + } + + spin_unlock_irqrestore(ioa_cfg->host->host_lock, host_lock_flags); + + LEAVE; + return rc; +} + +/** + * ipr_free_cmd_blks - Frees command blocks allocated for an adapter + * @ioa_cfg: ioa config struct + * + * Return value: + * none + **/ +static void ipr_free_cmd_blks(struct ipr_ioa_cfg *ioa_cfg) +{ + int i; + + for (i = 0; i < IPR_NUM_CMD_BLKS; i++) { + if (ioa_cfg->ipr_cmnd_list[i]) + pci_pool_free(ioa_cfg->ipr_cmd_pool, + ioa_cfg->ipr_cmnd_list[i], + ioa_cfg->ipr_cmnd_list_dma[i]); + + ioa_cfg->ipr_cmnd_list[i] = NULL; + } + + if (ioa_cfg->ipr_cmd_pool) + pci_pool_destroy (ioa_cfg->ipr_cmd_pool); + + ioa_cfg->ipr_cmd_pool = NULL; +} + +/** + * ipr_free_mem - Frees memory allocated for an adapter + * @ioa_cfg: ioa cfg struct + * + * Return value: + * nothing + **/ +static void ipr_free_mem(struct ipr_ioa_cfg *ioa_cfg) +{ + int i; + + kfree(ioa_cfg->res_entries); + pci_free_consistent(ioa_cfg->pdev, sizeof(struct ipr_misc_cbs), + ioa_cfg->vpd_cbs, ioa_cfg->vpd_cbs_dma); + ipr_free_cmd_blks(ioa_cfg); + pci_free_consistent(ioa_cfg->pdev, sizeof(u32) * IPR_NUM_CMD_BLKS, + ioa_cfg->host_rrq, ioa_cfg->host_rrq_dma); + pci_free_consistent(ioa_cfg->pdev, sizeof(struct ipr_config_table), + ioa_cfg->cfg_table, + ioa_cfg->cfg_table_dma); + + for (i = 0; i < IPR_NUM_HCAMS; i++) { + pci_free_consistent(ioa_cfg->pdev, + sizeof(struct ipr_hostrcb), + ioa_cfg->hostrcb[i], + ioa_cfg->hostrcb_dma[i]); + } + + ipr_free_dump(ioa_cfg); + kfree(ioa_cfg->saved_mode_pages); + kfree(ioa_cfg->trace); +} + +/** + * ipr_free_all_resources - Free all allocated resources for an adapter. + * @ipr_cmd: ipr command struct + * + * This function frees all allocated resources for the + * specified adapter. + * + * Return value: + * none + **/ +static void ipr_free_all_resources(struct ipr_ioa_cfg *ioa_cfg) +{ + ENTER; + free_irq(ioa_cfg->pdev->irq, ioa_cfg); + iounmap((void *) ioa_cfg->hdw_dma_regs); + release_mem_region(ioa_cfg->hdw_dma_regs_pci, + pci_resource_len(ioa_cfg->pdev, 0)); + ipr_free_mem(ioa_cfg); + scsi_host_put(ioa_cfg->host); + LEAVE; +} + +/** + * ipr_alloc_cmd_blks - Allocate command blocks for an adapter + * @ioa_cfg: ioa config struct + * + * Return value: + * 0 on success / -ENOMEM on allocation failure + **/ +static int __devinit ipr_alloc_cmd_blks(struct ipr_ioa_cfg *ioa_cfg) +{ + struct ipr_cmnd *ipr_cmd; + struct ipr_ioarcb *ioarcb; + u32 dma_addr; + int i; + + ioa_cfg->ipr_cmd_pool = pci_pool_create (IPR_NAME, ioa_cfg->pdev, + sizeof(struct ipr_cmnd), 8, 0); + + if (!ioa_cfg->ipr_cmd_pool) + return -ENOMEM; + + for (i = 0; i < IPR_NUM_CMD_BLKS; i++) { + ipr_cmd = pci_pool_alloc (ioa_cfg->ipr_cmd_pool, SLAB_KERNEL, &dma_addr); + + if (!ipr_cmd) { + ipr_free_cmd_blks(ioa_cfg); + return -ENOMEM; + } + + memset(ipr_cmd, 0, sizeof(*ipr_cmd)); + ioa_cfg->ipr_cmnd_list[i] = ipr_cmd; + ioa_cfg->ipr_cmnd_list_dma[i] = dma_addr; + + ioarcb = &ipr_cmd->ioarcb; + ioarcb->ioarcb_host_pci_addr = cpu_to_be32(dma_addr); + ioarcb->host_response_handle = cpu_to_be32(i << 2); + ioarcb->write_ioadl_addr = + cpu_to_be32(dma_addr + offsetof(struct ipr_cmnd, ioadl)); + ioarcb->read_ioadl_addr = ioarcb->write_ioadl_addr; + ioarcb->ioasa_host_pci_addr = + cpu_to_be32(dma_addr + offsetof(struct ipr_cmnd, ioasa)); + ioarcb->ioasa_len = cpu_to_be16(sizeof(struct ipr_ioasa)); + ipr_cmd->cmd_index = i; + ipr_cmd->ioa_cfg = ioa_cfg; + ipr_cmd->sense_buffer_dma = dma_addr + + offsetof(struct ipr_cmnd, sense_buffer); + + list_add_tail(&ipr_cmd->queue, &ioa_cfg->free_q); + } + + return 0; +} + +/** + * ipr_alloc_mem - Allocate memory for an adapter + * @ioa_cfg: ioa config struct + * + * Return value: + * 0 on success / non-zero for error + **/ +static int __devinit ipr_alloc_mem(struct ipr_ioa_cfg *ioa_cfg) +{ + int i; + + ENTER; + ioa_cfg->res_entries = kmalloc(sizeof(struct ipr_resource_entry) * + IPR_MAX_PHYSICAL_DEVS, GFP_KERNEL); + + if (!ioa_cfg->res_entries) + goto cleanup; + + memset(ioa_cfg->res_entries, 0, + sizeof(struct ipr_resource_entry) * IPR_MAX_PHYSICAL_DEVS); + + for (i = 0; i < IPR_MAX_PHYSICAL_DEVS; i++) + list_add_tail(&ioa_cfg->res_entries[i].queue, &ioa_cfg->free_res_q); + + ioa_cfg->vpd_cbs = pci_alloc_consistent(ioa_cfg->pdev, + sizeof(struct ipr_misc_cbs), + &ioa_cfg->vpd_cbs_dma); + + if (!ioa_cfg->vpd_cbs) + goto cleanup; + + if (ipr_alloc_cmd_blks(ioa_cfg)) + goto cleanup; + + ioa_cfg->host_rrq = pci_alloc_consistent(ioa_cfg->pdev, + sizeof(u32) * IPR_NUM_CMD_BLKS, + &ioa_cfg->host_rrq_dma); + + if (!ioa_cfg->host_rrq) + goto cleanup; + + ioa_cfg->cfg_table = pci_alloc_consistent(ioa_cfg->pdev, + sizeof(struct ipr_config_table), + &ioa_cfg->cfg_table_dma); + + if (!ioa_cfg->cfg_table) + goto cleanup; + + for (i = 0; i < IPR_NUM_HCAMS; i++) { + ioa_cfg->hostrcb[i] = pci_alloc_consistent(ioa_cfg->pdev, + sizeof(struct ipr_hostrcb), + &ioa_cfg->hostrcb_dma[i]); + + if (!ioa_cfg->hostrcb[i]) + goto cleanup; + + memset(ioa_cfg->hostrcb[i], 0, sizeof(struct ipr_hostrcb)); + ioa_cfg->hostrcb[i]->hostrcb_dma = + ioa_cfg->hostrcb_dma[i] + offsetof(struct ipr_hostrcb, hcam); + list_add_tail(&ioa_cfg->hostrcb[i]->queue, &ioa_cfg->hostrcb_free_q); + } + + ioa_cfg->trace = kmalloc(sizeof(struct ipr_trace_entry) * + IPR_NUM_TRACE_ENTRIES, GFP_KERNEL); + + if (!ioa_cfg->trace) + goto cleanup; + + memset(ioa_cfg->trace, 0, + sizeof(struct ipr_trace_entry) * IPR_NUM_TRACE_ENTRIES); + + LEAVE; + return 0; + +cleanup: + ipr_free_mem(ioa_cfg); + + LEAVE; + return -ENOMEM; +} + +/** + * ipr_initialize_bus_attr - Initialize SCSI bus attributes to default values + * @ioa_cfg: ioa config struct + * + * Return value: + * none + **/ +static void __devinit ipr_initialize_bus_attr(struct ipr_ioa_cfg *ioa_cfg) +{ + int i; + + for (i = 0; i < IPR_MAX_NUM_BUSES; i++) { + ioa_cfg->bus_attr[i].bus = i; + ioa_cfg->bus_attr[i].qas_enabled = 0; + ioa_cfg->bus_attr[i].bus_width = IPR_DEFAULT_BUS_WIDTH; + if (ipr_max_speed < ARRAY_SIZE(ipr_max_bus_speeds)) + ioa_cfg->bus_attr[i].max_xfer_rate = ipr_max_bus_speeds[ipr_max_speed]; + else + ioa_cfg->bus_attr[i].max_xfer_rate = IPR_U160_SCSI_RATE; + } +} + +/** + * ipr_init_ioa_cfg - Initialize IOA config struct + * @ioa_cfg: ioa config struct + * @host: scsi host struct + * @pdev: PCI dev struct + * + * Return value: + * none + **/ +static void __devinit ipr_init_ioa_cfg(struct ipr_ioa_cfg *ioa_cfg, + struct Scsi_Host *host, struct pci_dev *pdev) +{ + ioa_cfg->host = host; + ioa_cfg->pdev = pdev; + ioa_cfg->log_level = ipr_log_level; + sprintf(ioa_cfg->eye_catcher, IPR_EYECATCHER); + sprintf(ioa_cfg->trace_start, IPR_TRACE_START_LABEL); + sprintf(ioa_cfg->ipr_free_label, IPR_FREEQ_LABEL); + sprintf(ioa_cfg->ipr_pending_label, IPR_PENDQ_LABEL); + sprintf(ioa_cfg->cfg_table_start, IPR_CFG_TBL_START); + sprintf(ioa_cfg->resource_table_label, IPR_RES_TABLE_LABEL); + sprintf(ioa_cfg->ipr_hcam_label, IPR_HCAM_LABEL); + sprintf(ioa_cfg->ipr_cmd_label, IPR_CMD_LABEL); + + INIT_LIST_HEAD(&ioa_cfg->free_q); + INIT_LIST_HEAD(&ioa_cfg->pending_q); + INIT_LIST_HEAD(&ioa_cfg->hostrcb_free_q); + INIT_LIST_HEAD(&ioa_cfg->hostrcb_pending_q); + INIT_LIST_HEAD(&ioa_cfg->free_res_q); + INIT_LIST_HEAD(&ioa_cfg->used_res_q); + INIT_WORK(&ioa_cfg->work_q, ipr_worker_thread, ioa_cfg); + init_waitqueue_head(&ioa_cfg->reset_wait_q); + ioa_cfg->sdt_state = INACTIVE; + + ipr_initialize_bus_attr(ioa_cfg); + + host->max_id = IPR_MAX_NUM_TARGETS_PER_BUS; + host->max_lun = IPR_MAX_NUM_LUNS_PER_TARGET; + host->max_channel = IPR_MAX_BUS_TO_SCAN; + host->unique_id = host->host_no; + host->max_cmd_len = IPR_MAX_CDB_LEN; + pci_set_drvdata(pdev, ioa_cfg); + + memcpy(&ioa_cfg->regs, &ioa_cfg->chip_cfg->regs, sizeof(ioa_cfg->regs)); + + ioa_cfg->regs.set_interrupt_mask_reg += ioa_cfg->hdw_dma_regs; + ioa_cfg->regs.clr_interrupt_mask_reg += ioa_cfg->hdw_dma_regs; + ioa_cfg->regs.sense_interrupt_mask_reg += ioa_cfg->hdw_dma_regs; + ioa_cfg->regs.clr_interrupt_reg += ioa_cfg->hdw_dma_regs; + ioa_cfg->regs.sense_interrupt_reg += ioa_cfg->hdw_dma_regs; + ioa_cfg->regs.ioarrin_reg += ioa_cfg->hdw_dma_regs; + ioa_cfg->regs.sense_uproc_interrupt_reg += ioa_cfg->hdw_dma_regs; + ioa_cfg->regs.set_uproc_interrupt_reg += ioa_cfg->hdw_dma_regs; + ioa_cfg->regs.clr_uproc_interrupt_reg += ioa_cfg->hdw_dma_regs; +} + +/** + * ipr_probe_ioa - Allocates memory and does first stage of initialization + * @pdev: PCI device struct + * @dev_id: PCI device id struct + * + * Return value: + * 0 on success / non-zero on failure + **/ +static int __devinit ipr_probe_ioa(struct pci_dev *pdev, + const struct pci_device_id *dev_id) +{ + struct ipr_ioa_cfg *ioa_cfg; + struct Scsi_Host *host; + unsigned long ipr_regs, ipr_regs_pci; + u32 rc = PCIBIOS_SUCCESSFUL; + + ENTER; + + if ((rc = pci_enable_device(pdev))) { + dev_err(&pdev->dev, "Cannot enable adapter\n"); + return rc; + } + + dev_info(&pdev->dev, "Found IOA with IRQ: %d\n", pdev->irq); + + host = scsi_host_alloc(&driver_template, sizeof(*ioa_cfg)); + + if (!host) { + dev_err(&pdev->dev, "call to scsi_host_alloc failed!\n"); + return -ENOMEM; + } + + ioa_cfg = (struct ipr_ioa_cfg *)host->hostdata; + memset(ioa_cfg, 0, sizeof(struct ipr_ioa_cfg)); + + ioa_cfg->chip_cfg = (const struct ipr_chip_cfg_t *)dev_id->driver_data; + + ipr_regs_pci = pci_resource_start(pdev, 0); + + if (!request_mem_region(ipr_regs_pci, + pci_resource_len(pdev, 0), IPR_NAME)) { + dev_err(&pdev->dev, + "Couldn't register memory range of registers\n"); + scsi_host_put(host); + return -ENOMEM; + } + + ipr_regs = (unsigned long)ioremap(ipr_regs_pci, + pci_resource_len(pdev, 0)); + + if (!ipr_regs) { + dev_err(&pdev->dev, + "Couldn't map memory range of registers\n"); + release_mem_region(ipr_regs_pci, pci_resource_len(pdev, 0)); + scsi_host_put(host); + return -ENOMEM; + } + + ioa_cfg->hdw_dma_regs = ipr_regs; + ioa_cfg->hdw_dma_regs_pci = ipr_regs_pci; + ioa_cfg->ioa_mailbox = ioa_cfg->chip_cfg->mailbox + ipr_regs; + + ipr_init_ioa_cfg(ioa_cfg, host, pdev); + + pci_set_master(pdev); + rc = pci_set_dma_mask(pdev, 0xffffffff); + + if (rc != PCIBIOS_SUCCESSFUL) { + dev_err(&pdev->dev, "Failed to set PCI DMA mask\n"); + rc = -EIO; + goto cleanup_nomem; + } + + rc = pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, + ioa_cfg->chip_cfg->cache_line_size); + + if (rc != PCIBIOS_SUCCESSFUL) { + dev_err(&pdev->dev, "Write of cache line size failed\n"); + rc = -EIO; + goto cleanup_nomem; + } + + /* Save away PCI config space for use following IOA reset */ + rc = pci_save_state(pdev, ioa_cfg->pci_cfg_buf); + + if (rc != PCIBIOS_SUCCESSFUL) { + dev_err(&pdev->dev, "Failed to save PCI config space\n"); + rc = -EIO; + goto cleanup_nomem; + } + + if ((rc = ipr_save_pcix_cmd_reg(ioa_cfg))) + goto cleanup_nomem; + + if ((rc = ipr_set_pcix_cmd_reg(ioa_cfg))) + goto cleanup_nomem; + + if ((rc = ipr_alloc_mem(ioa_cfg))) + goto cleanup; + + ipr_mask_and_clear_interrupts(ioa_cfg, ~IPR_PCII_IOA_TRANS_TO_OPER); + rc = request_irq(pdev->irq, ipr_isr, SA_SHIRQ, IPR_NAME, ioa_cfg); + + if (rc) { + dev_err(&pdev->dev, "Couldn't register IRQ %d! rc=%d\n", + pdev->irq, rc); + goto cleanup_nolog; + } + + spin_lock(&ipr_driver_lock); + list_add_tail(&ioa_cfg->queue, &ipr_ioa_head); + spin_unlock(&ipr_driver_lock); + + LEAVE; + return 0; + +cleanup: + dev_err(&pdev->dev, "Couldn't allocate enough memory for device driver!\n"); +cleanup_nolog: + ipr_free_mem(ioa_cfg); +cleanup_nomem: + iounmap((void *) ipr_regs); + release_mem_region(ipr_regs_pci, pci_resource_len(pdev, 0)); + scsi_host_put(host); + + return rc; +} + +/** + * ipr_scan_vsets - Scans for VSET devices + * @ioa_cfg: ioa config struct + * + * Description: Since the VSET resources do not follow SAM in that we can have + * sparse LUNs with no LUN 0, we have to scan for these ourselves. + * + * Return value: + * none + **/ +static void ipr_scan_vsets(struct ipr_ioa_cfg *ioa_cfg) +{ + int target, lun; + + for (target = 0; target < IPR_MAX_NUM_TARGETS_PER_BUS; target++) + for (lun = 0; lun < IPR_MAX_NUM_VSET_LUNS_PER_TARGET; lun++ ) + scsi_add_device(ioa_cfg->host, IPR_VSET_BUS, target, lun); +} + +/** + * ipr_initiate_ioa_bringdown - Bring down an adapter + * @ioa_cfg: ioa config struct + * @shutdown_type: shutdown type + * + * Description: This function will initiate bringing down the adapter. + * This consists of issuing an IOA shutdown to the adapter + * to flush the cache, and running BIST. + * If the caller needs to wait on the completion of the reset, + * the caller must sleep on the reset_wait_q. + * + * Return value: + * none + **/ +static void ipr_initiate_ioa_bringdown(struct ipr_ioa_cfg *ioa_cfg, + enum ipr_shutdown_type shutdown_type) +{ + ENTER; + if (ioa_cfg->sdt_state == WAIT_FOR_DUMP) + ioa_cfg->sdt_state = ABORT_DUMP; + ioa_cfg->reset_retries = 0; + ioa_cfg->in_ioa_bringdown = 1; + ipr_initiate_ioa_reset(ioa_cfg, shutdown_type); + LEAVE; +} + +/** + * __ipr_remove - Remove a single adapter + * @pdev: pci device struct + * + * Adapter hot plug remove entry point. + * + * Return value: + * none + **/ +static void __ipr_remove(struct pci_dev *pdev) +{ + unsigned long host_lock_flags = 0; + struct ipr_ioa_cfg *ioa_cfg = pci_get_drvdata(pdev); + ENTER; + + spin_lock_irqsave(ioa_cfg->host->host_lock, host_lock_flags); + ipr_initiate_ioa_bringdown(ioa_cfg, IPR_SHUTDOWN_NORMAL); + + spin_unlock_irqrestore(ioa_cfg->host->host_lock, host_lock_flags); + wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload); + spin_lock_irqsave(ioa_cfg->host->host_lock, host_lock_flags); + + spin_lock(&ipr_driver_lock); + list_del(&ioa_cfg->queue); + spin_unlock(&ipr_driver_lock); + + if (ioa_cfg->sdt_state == ABORT_DUMP) + ioa_cfg->sdt_state = WAIT_FOR_DUMP; + spin_unlock_irqrestore(ioa_cfg->host->host_lock, host_lock_flags); + + ipr_free_all_resources(ioa_cfg); + + LEAVE; +} + +/** + * ipr_remove - IOA hot plug remove entry point + * @pdev: pci device struct + * + * Adapter hot plug remove entry point. + * + * Return value: + * none + **/ +static void ipr_remove(struct pci_dev *pdev) +{ + struct ipr_ioa_cfg *ioa_cfg = pci_get_drvdata(pdev); + + ENTER; + + ioa_cfg->allow_cmds = 0; + flush_scheduled_work(); + ipr_remove_trace_file(&ioa_cfg->host->shost_classdev.kobj, + &ipr_trace_attr); + ipr_remove_dump_file(&ioa_cfg->host->shost_classdev.kobj, + &ipr_dump_attr); + scsi_remove_host(ioa_cfg->host); + + __ipr_remove(pdev); + + LEAVE; +} + +/** + * ipr_probe - Adapter hot plug add entry point + * + * Return value: + * 0 on success / non-zero on failure + **/ +static int __devinit ipr_probe(struct pci_dev *pdev, + const struct pci_device_id *dev_id) +{ + struct ipr_ioa_cfg *ioa_cfg; + int rc; + + rc = ipr_probe_ioa(pdev, dev_id); + + if (rc) + return rc; + + ioa_cfg = pci_get_drvdata(pdev); + rc = ipr_probe_ioa_part2(ioa_cfg); + + if (rc) { + __ipr_remove(pdev); + return rc; + } + + rc = scsi_add_host(ioa_cfg->host, &pdev->dev); + + if (rc) { + __ipr_remove(pdev); + return rc; + } + + rc = ipr_create_trace_file(&ioa_cfg->host->shost_classdev.kobj, + &ipr_trace_attr); + + if (rc) { + scsi_remove_host(ioa_cfg->host); + __ipr_remove(pdev); + return rc; + } + + rc = ipr_create_dump_file(&ioa_cfg->host->shost_classdev.kobj, + &ipr_dump_attr); + + if (rc) { + ipr_remove_trace_file(&ioa_cfg->host->shost_classdev.kobj, + &ipr_trace_attr); + scsi_remove_host(ioa_cfg->host); + __ipr_remove(pdev); + return rc; + } + + scsi_scan_host(ioa_cfg->host); + ipr_scan_vsets(ioa_cfg); + scsi_add_device(ioa_cfg->host, IPR_IOA_BUS, IPR_IOA_TARGET, IPR_IOA_LUN); + ioa_cfg->allow_ml_add_del = 1; + schedule_work(&ioa_cfg->work_q); + return 0; +} + +/** + * ipr_shutdown - Shutdown handler. + * @dev: device struct + * + * This function is invoked upon system shutdown/reboot. It will issue + * an adapter shutdown to the adapter to flush the write cache. + * + * Return value: + * none + **/ +static void ipr_shutdown(struct device *dev) +{ + struct ipr_ioa_cfg *ioa_cfg = pci_get_drvdata(to_pci_dev(dev)); + unsigned long lock_flags = 0; + + spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); + ipr_initiate_ioa_bringdown(ioa_cfg, IPR_SHUTDOWN_NORMAL); + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload); +} + +static struct pci_device_id ipr_pci_table[] __devinitdata = { + { PCI_VENDOR_ID_MYLEX, PCI_DEVICE_ID_IBM_GEMSTONE, + PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_5702, + 0, 0, (kernel_ulong_t)&ipr_chip_cfg[0] }, + { PCI_VENDOR_ID_MYLEX, PCI_DEVICE_ID_IBM_GEMSTONE, + PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_572E, + 0, 0, (kernel_ulong_t)&ipr_chip_cfg[0] }, + { PCI_VENDOR_ID_MYLEX, PCI_DEVICE_ID_IBM_GEMSTONE, + PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_5703, + 0, 0, (kernel_ulong_t)&ipr_chip_cfg[0] }, + { PCI_VENDOR_ID_MYLEX, PCI_DEVICE_ID_IBM_GEMSTONE, + PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_573D, + 0, 0, (kernel_ulong_t)&ipr_chip_cfg[0] }, + { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_SNIPE, + PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_2780, + 0, 0, (kernel_ulong_t)&ipr_chip_cfg[1] }, + { } +}; +MODULE_DEVICE_TABLE(pci, ipr_pci_table); + +static struct pci_driver ipr_driver = { + .name = IPR_NAME, + .id_table = ipr_pci_table, + .probe = ipr_probe, + .remove = ipr_remove, + .driver = { + .shutdown = ipr_shutdown, + }, +}; + +/** + * ipr_init - Module entry point + * + * Return value: + * 0 on success / non-zero on failure + **/ +static int __init ipr_init(void) +{ + ipr_info("IBM Power RAID SCSI Device Driver version: %s %s\n", + IPR_DRIVER_VERSION, IPR_DRIVER_DATE); + + pci_register_driver(&ipr_driver); + + return 0; +} + +/** + * ipr_exit - Module unload + * + * Module unload entry point. + * + * Return value: + * none + **/ +static void __exit ipr_exit(void) +{ + pci_unregister_driver(&ipr_driver); +} + +module_init(ipr_init); +module_exit(ipr_exit); diff --git a/drivers/scsi/ipr.h b/drivers/scsi/ipr.h new file mode 100644 index 000000000..468c80796 --- /dev/null +++ b/drivers/scsi/ipr.h @@ -0,0 +1,1252 @@ +/* + * ipr.h -- driver for IBM Power Linux RAID adapters + * + * Written By: Brian King, IBM Corporation + * + * Copyright (C) 2003, 2004 IBM Corporation + * + * 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 + * + */ + +#ifndef _IPR_H +#define _IPR_H + +#include +#include +#include +#include +#include +#ifdef CONFIG_KDB +#include +#endif + +/* + * Literals + */ +#define IPR_DRIVER_VERSION "2.0.7" +#define IPR_DRIVER_DATE "(May 21, 2004)" + +/* + * IPR_DBG_TRACE: Setting this to 1 will turn on some general function tracing + * resulting in a bunch of extra debugging printks to the console + * + * IPR_DEBUG: Setting this to 1 will turn on some error path tracing. + * Enables the ipr_trace macro. + */ +#ifdef IPR_DEBUG_ALL +#define IPR_DEBUG 1 +#define IPR_DBG_TRACE 1 +#else +#define IPR_DEBUG 0 +#define IPR_DBG_TRACE 0 +#endif + +/* + * IPR_MAX_CMD_PER_LUN: This defines the maximum number of outstanding + * ops per device for devices not running tagged command queuing. + * This can be adjusted at runtime through sysfs device attributes. + */ +#define IPR_MAX_CMD_PER_LUN 6 + +/* + * IPR_NUM_BASE_CMD_BLKS: This defines the maximum number of + * ops the mid-layer can send to the adapter. + */ +#define IPR_NUM_BASE_CMD_BLKS 100 + +#define IPR_SUBS_DEV_ID_2780 0x0264 +#define IPR_SUBS_DEV_ID_5702 0x0266 +#define IPR_SUBS_DEV_ID_5703 0x0278 +#define IPR_SUBS_DEV_ID_572E 0x02D3 +#define IPR_SUBS_DEV_ID_573D 0x02D4 + +#define IPR_NAME "ipr" + +/* + * Return codes + */ +#define IPR_RC_JOB_CONTINUE 1 +#define IPR_RC_JOB_RETURN 2 + +/* + * IOASCs + */ +#define IPR_IOASC_NR_INIT_CMD_REQUIRED 0x02040200 +#define IPR_IOASC_SYNC_REQUIRED 0x023f0000 +#define IPR_IOASC_MED_DO_NOT_REALLOC 0x03110C00 +#define IPR_IOASC_HW_SEL_TIMEOUT 0x04050000 +#define IPR_IOASC_HW_DEV_BUS_STATUS 0x04448500 +#define IPR_IOASC_IOASC_MASK 0xFFFFFF00 +#define IPR_IOASC_SCSI_STATUS_MASK 0x000000FF +#define IPR_IOASC_IR_RESOURCE_HANDLE 0x05250000 +#define IPR_IOASC_BUS_WAS_RESET 0x06290000 +#define IPR_IOASC_BUS_WAS_RESET_BY_OTHER 0x06298000 +#define IPR_IOASC_ABORTED_CMD_TERM_BY_HOST 0x0B5A0000 + +#define IPR_FIRST_DRIVER_IOASC 0x10000000 +#define IPR_IOASC_IOA_WAS_RESET 0x10000001 +#define IPR_IOASC_PCI_ACCESS_ERROR 0x10000002 + +#define IPR_NUM_LOG_HCAMS 2 +#define IPR_NUM_CFG_CHG_HCAMS 2 +#define IPR_NUM_HCAMS (IPR_NUM_LOG_HCAMS + IPR_NUM_CFG_CHG_HCAMS) +#define IPR_MAX_NUM_TARGETS_PER_BUS 0x10 +#define IPR_MAX_NUM_LUNS_PER_TARGET 256 +#define IPR_MAX_NUM_VSET_LUNS_PER_TARGET 8 +#define IPR_VSET_BUS 0xff +#define IPR_IOA_BUS 0xff +#define IPR_IOA_TARGET 0xff +#define IPR_IOA_LUN 0xff +#define IPR_MAX_NUM_BUSES 4 +#define IPR_MAX_BUS_TO_SCAN IPR_MAX_NUM_BUSES + +#define IPR_NUM_RESET_RELOAD_RETRIES 3 + +/* We need resources for HCAMS, IOA reset, IOA bringdown, and ERP */ +#define IPR_NUM_INTERNAL_CMD_BLKS (IPR_NUM_HCAMS + \ + ((IPR_NUM_RESET_RELOAD_RETRIES + 1) * 2) + 3) + +#define IPR_MAX_COMMANDS IPR_NUM_BASE_CMD_BLKS +#define IPR_NUM_CMD_BLKS (IPR_NUM_BASE_CMD_BLKS + \ + IPR_NUM_INTERNAL_CMD_BLKS) + +#define IPR_MAX_PHYSICAL_DEVS 192 + +#define IPR_MAX_SGLIST 64 +#define IPR_MAX_SECTORS 512 +#define IPR_MAX_CDB_LEN 16 + +#define IPR_DEFAULT_BUS_WIDTH 16 +#define IPR_80MBs_SCSI_RATE ((80 * 10) / (IPR_DEFAULT_BUS_WIDTH / 8)) +#define IPR_U160_SCSI_RATE ((160 * 10) / (IPR_DEFAULT_BUS_WIDTH / 8)) +#define IPR_U320_SCSI_RATE ((320 * 10) / (IPR_DEFAULT_BUS_WIDTH / 8)) +#define IPR_MAX_SCSI_RATE(width) ((320 * 10) / ((width) / 8)) + +#define IPR_IOA_RES_HANDLE 0xffffffff +#define IPR_IOA_RES_ADDR 0x00ffffff + +/* + * Adapter Commands + */ +#define IPR_RESET_DEVICE 0xC3 +#define IPR_RESET_TYPE_SELECT 0x80 +#define IPR_LUN_RESET 0x40 +#define IPR_TARGET_RESET 0x20 +#define IPR_BUS_RESET 0x10 +#define IPR_ID_HOST_RR_Q 0xC4 +#define IPR_QUERY_IOA_CONFIG 0xC5 +#define IPR_ABORT_TASK 0xC7 +#define IPR_CANCEL_ALL_REQUESTS 0xCE +#define IPR_HOST_CONTROLLED_ASYNC 0xCF +#define IPR_HCAM_CDB_OP_CODE_CONFIG_CHANGE 0x01 +#define IPR_HCAM_CDB_OP_CODE_LOG_DATA 0x02 +#define IPR_SET_SUPPORTED_DEVICES 0xFB +#define IPR_IOA_SHUTDOWN 0xF7 +#define IPR_WR_BUF_DOWNLOAD_AND_SAVE 0x05 + +/* + * Timeouts + */ +#define IPR_SHUTDOWN_TIMEOUT (10 * 60 * HZ) +#define IPR_VSET_RW_TIMEOUT (2 * 60 * HZ) +#define IPR_ABBREV_SHUTDOWN_TIMEOUT (10 * HZ) +#define IPR_DEVICE_RESET_TIMEOUT (30 * HZ) +#define IPR_CANCEL_ALL_TIMEOUT (30 * HZ) +#define IPR_ABORT_TASK_TIMEOUT (30 * HZ) +#define IPR_INTERNAL_TIMEOUT (30 * HZ) +#define IPR_WRITE_BUFFER_TIMEOUT (10 * 60 * HZ) +#define IPR_SET_SUP_DEVICE_TIMEOUT (2 * 60 * HZ) +#define IPR_REQUEST_SENSE_TIMEOUT (10 * HZ) +#define IPR_OPERATIONAL_TIMEOUT (5 * 60 * HZ) +#define IPR_WAIT_FOR_RESET_TIMEOUT (2 * HZ) +#define IPR_CHECK_FOR_RESET_TIMEOUT (HZ / 10) +#define IPR_WAIT_FOR_BIST_TIMEOUT (2 * HZ) +#define IPR_DUMP_TIMEOUT (15 * HZ) + +/* + * SCSI Literals + */ +#define IPR_VENDOR_ID_LEN 8 +#define IPR_PROD_ID_LEN 16 +#define IPR_SERIAL_NUM_LEN 8 + +/* + * Hardware literals + */ +#define IPR_FMT2_MBX_ADDR_MASK 0x0fffffff +#define IPR_FMT2_MBX_BAR_SEL_MASK 0xf0000000 +#define IPR_FMT2_MKR_BAR_SEL_SHIFT 28 +#define IPR_GET_FMT2_BAR_SEL(mbx) \ +(((mbx) & IPR_FMT2_MBX_BAR_SEL_MASK) >> IPR_FMT2_MKR_BAR_SEL_SHIFT) +#define IPR_SDT_FMT2_BAR0_SEL 0x0 +#define IPR_SDT_FMT2_BAR1_SEL 0x1 +#define IPR_SDT_FMT2_BAR2_SEL 0x2 +#define IPR_SDT_FMT2_BAR3_SEL 0x3 +#define IPR_SDT_FMT2_BAR4_SEL 0x4 +#define IPR_SDT_FMT2_BAR5_SEL 0x5 +#define IPR_SDT_FMT2_EXP_ROM_SEL 0x8 +#define IPR_FMT2_SDT_READY_TO_USE 0xC4D4E3F2 +#define IPR_DOORBELL 0x82800000 + +#define IPR_PCII_IOA_TRANS_TO_OPER (0x80000000 >> 0) +#define IPR_PCII_IOARCB_XFER_FAILED (0x80000000 >> 3) +#define IPR_PCII_IOA_UNIT_CHECKED (0x80000000 >> 4) +#define IPR_PCII_NO_HOST_RRQ (0x80000000 >> 5) +#define IPR_PCII_CRITICAL_OPERATION (0x80000000 >> 6) +#define IPR_PCII_IO_DEBUG_ACKNOWLEDGE (0x80000000 >> 7) +#define IPR_PCII_IOARRIN_LOST (0x80000000 >> 27) +#define IPR_PCII_MMIO_ERROR (0x80000000 >> 28) +#define IPR_PCII_PROC_ERR_STATE (0x80000000 >> 29) +#define IPR_PCII_HRRQ_UPDATED (0x80000000 >> 30) +#define IPR_PCII_CORE_ISSUED_RST_REQ (0x80000000 >> 31) + +#define IPR_PCII_ERROR_INTERRUPTS \ +(IPR_PCII_IOARCB_XFER_FAILED | IPR_PCII_IOA_UNIT_CHECKED | \ +IPR_PCII_NO_HOST_RRQ | IPR_PCII_IOARRIN_LOST | IPR_PCII_MMIO_ERROR) + +#define IPR_PCII_OPER_INTERRUPTS \ +(IPR_PCII_ERROR_INTERRUPTS | IPR_PCII_HRRQ_UPDATED | IPR_PCII_IOA_TRANS_TO_OPER) + +#define IPR_UPROCI_RESET_ALERT (0x80000000 >> 7) +#define IPR_UPROCI_IO_DEBUG_ALERT (0x80000000 >> 9) + +#define IPR_LDUMP_MAX_LONG_ACK_DELAY_IN_USEC 200000 /* 200 ms */ +#define IPR_LDUMP_MAX_SHORT_ACK_DELAY_IN_USEC 200000 /* 200 ms */ + +/* + * Dump literals + */ +#define IPR_MAX_IOA_DUMP_SIZE (4 * 1024 * 1024) +#define IPR_NUM_SDT_ENTRIES 511 +#define IPR_MAX_NUM_DUMP_PAGES ((IPR_MAX_IOA_DUMP_SIZE / PAGE_SIZE) + 1) + +/* + * Misc literals + */ +#define IPR_NUM_IOADL_ENTRIES IPR_MAX_SGLIST + +/* + * Adapter interface types + */ + +struct ipr_res_addr { + u8 reserved; + u8 bus; + u8 target; + u8 lun; +#define IPR_GET_PHYS_LOC(res_addr) \ + (((res_addr).bus << 16) | ((res_addr).target << 8) | (res_addr).lun) +}__attribute__((packed, aligned (4))); + +struct ipr_std_inq_vpids { + u8 vendor_id[IPR_VENDOR_ID_LEN]; + u8 product_id[IPR_PROD_ID_LEN]; +}__attribute__((packed)); + +struct ipr_std_inq_data { + u8 peri_qual_dev_type; +#define IPR_STD_INQ_PERI_QUAL(peri) ((peri) >> 5) +#define IPR_STD_INQ_PERI_DEV_TYPE(peri) ((peri) & 0x1F) + + u8 removeable_medium_rsvd; +#define IPR_STD_INQ_REMOVEABLE_MEDIUM 0x80 + +#define IPR_IS_DASD_DEVICE(std_inq) \ +((IPR_STD_INQ_PERI_DEV_TYPE((std_inq).peri_qual_dev_type) == TYPE_DISK) && \ +!(((std_inq).removeable_medium_rsvd) & IPR_STD_INQ_REMOVEABLE_MEDIUM)) + +#define IPR_IS_SES_DEVICE(std_inq) \ +(IPR_STD_INQ_PERI_DEV_TYPE((std_inq).peri_qual_dev_type) == TYPE_ENCLOSURE) + + u8 version; + u8 aen_naca_fmt; + u8 additional_len; + u8 sccs_rsvd; + u8 bq_enc_multi; + u8 sync_cmdq_flags; + + struct ipr_std_inq_vpids vpids; + + u8 ros_rsvd_ram_rsvd[4]; + + u8 serial_num[IPR_SERIAL_NUM_LEN]; +}__attribute__ ((packed)); + +struct ipr_config_table_entry { + u8 service_level; + u8 array_id; + u8 flags; +#define IPR_IS_IOA_RESOURCE 0x80 +#define IPR_IS_ARRAY_MEMBER 0x20 +#define IPR_IS_HOT_SPARE 0x10 + + u8 rsvd_subtype; +#define IPR_RES_SUBTYPE(res) (((res)->cfgte.rsvd_subtype) & 0x0f) +#define IPR_SUBTYPE_AF_DASD 0 +#define IPR_SUBTYPE_GENERIC_SCSI 1 +#define IPR_SUBTYPE_VOLUME_SET 2 + + struct ipr_res_addr res_addr; + u32 res_handle; + u32 reserved4[2]; + struct ipr_std_inq_data std_inq_data; +}__attribute__ ((packed, aligned (4))); + +struct ipr_config_table_hdr { + u8 num_entries; + u8 flags; +#define IPR_UCODE_DOWNLOAD_REQ 0x10 + u16 reserved; +}__attribute__((packed, aligned (4))); + +struct ipr_config_table { + struct ipr_config_table_hdr hdr; + struct ipr_config_table_entry dev[IPR_MAX_PHYSICAL_DEVS]; +}__attribute__((packed, aligned (4))); + +struct ipr_hostrcb_cfg_ch_not { + struct ipr_config_table_entry cfgte; + u8 reserved[936]; +}__attribute__((packed, aligned (4))); + +struct ipr_supported_device { + u16 data_length; + u8 reserved; + u8 num_records; + struct ipr_std_inq_vpids vpids; + u8 reserved2[16]; +}__attribute__((packed, aligned (4))); + +/* Command packet structure */ +struct ipr_cmd_pkt { + u16 reserved; /* Reserved by IOA */ + u8 request_type; +#define IPR_RQTYPE_SCSICDB 0x00 +#define IPR_RQTYPE_IOACMD 0x01 +#define IPR_RQTYPE_HCAM 0x02 + + u8 luntar_luntrn; + + u8 flags_hi; +#define IPR_FLAGS_HI_WRITE_NOT_READ 0x80 +#define IPR_FLAGS_HI_NO_ULEN_CHK 0x20 +#define IPR_FLAGS_HI_SYNC_OVERRIDE 0x10 +#define IPR_FLAGS_HI_SYNC_COMPLETE 0x08 +#define IPR_FLAGS_HI_NO_LINK_DESC 0x04 + + u8 flags_lo; +#define IPR_FLAGS_LO_ALIGNED_BFR 0x20 +#define IPR_FLAGS_LO_DELAY_AFTER_RST 0x10 +#define IPR_FLAGS_LO_UNTAGGED_TASK 0x00 +#define IPR_FLAGS_LO_SIMPLE_TASK 0x02 +#define IPR_FLAGS_LO_ORDERED_TASK 0x04 +#define IPR_FLAGS_LO_HEAD_OF_Q_TASK 0x06 +#define IPR_FLAGS_LO_ACA_TASK 0x08 + + u8 cdb[16]; + u16 timeout; +}__attribute__ ((packed, aligned(4))); + +/* IOA Request Control Block 128 bytes */ +struct ipr_ioarcb { + u32 ioarcb_host_pci_addr; + u32 reserved; + u32 res_handle; + u32 host_response_handle; + u32 reserved1; + u32 reserved2; + u32 reserved3; + + u32 write_data_transfer_length; + u32 read_data_transfer_length; + u32 write_ioadl_addr; + u32 write_ioadl_len; + u32 read_ioadl_addr; + u32 read_ioadl_len; + + u32 ioasa_host_pci_addr; + u16 ioasa_len; + u16 reserved4; + + struct ipr_cmd_pkt cmd_pkt; + + u32 add_cmd_parms_len; + u32 add_cmd_parms[10]; +}__attribute__((packed, aligned (4))); + +struct ipr_ioadl_desc { + u32 flags_and_data_len; +#define IPR_IOADL_FLAGS_MASK 0xff000000 +#define IPR_IOADL_GET_FLAGS(x) (be32_to_cpu(x) & IPR_IOADL_FLAGS_MASK) +#define IPR_IOADL_DATA_LEN_MASK 0x00ffffff +#define IPR_IOADL_GET_DATA_LEN(x) (be32_to_cpu(x) & IPR_IOADL_DATA_LEN_MASK) +#define IPR_IOADL_FLAGS_READ 0x48000000 +#define IPR_IOADL_FLAGS_READ_LAST 0x49000000 +#define IPR_IOADL_FLAGS_WRITE 0x68000000 +#define IPR_IOADL_FLAGS_WRITE_LAST 0x69000000 +#define IPR_IOADL_FLAGS_LAST 0x01000000 + + u32 address; +}__attribute__((packed, aligned (8))); + +struct ipr_ioasa_vset { + u32 failing_lba_hi; + u32 failing_lba_lo; + u32 ioa_data[22]; +}__attribute__((packed, aligned (4))); + +struct ipr_ioasa_af_dasd { + u32 failing_lba; +}__attribute__((packed, aligned (4))); + +struct ipr_ioasa_gpdd { + u8 end_state; + u8 bus_phase; + u16 reserved; + u32 ioa_data[23]; +}__attribute__((packed, aligned (4))); + +struct ipr_ioasa_raw { + u32 ioa_data[24]; +}__attribute__((packed, aligned (4))); + +struct ipr_ioasa { + u32 ioasc; +#define IPR_IOASC_SENSE_KEY(ioasc) ((ioasc) >> 24) +#define IPR_IOASC_SENSE_CODE(ioasc) (((ioasc) & 0x00ff0000) >> 16) +#define IPR_IOASC_SENSE_QUAL(ioasc) (((ioasc) & 0x0000ff00) >> 8) +#define IPR_IOASC_SENSE_STATUS(ioasc) ((ioasc) & 0x000000ff) + + u16 ret_stat_len; /* Length of the returned IOASA */ + + u16 avail_stat_len; /* Total Length of status available. */ + + u32 residual_data_len; /* number of bytes in the host data */ + /* buffers that were not used by the IOARCB command. */ + + u32 ilid; +#define IPR_NO_ILID 0 +#define IPR_DRIVER_ILID 0xffffffff + + u32 fd_ioasc; + + u32 fd_phys_locator; + + u32 fd_res_handle; + + u32 ioasc_specific; /* status code specific field */ +#define IPR_IOASC_SPECIFIC_MASK 0x00ffffff +#define IPR_FIELD_POINTER_VALID (0x80000000 >> 8) +#define IPR_FIELD_POINTER_MASK 0x0000ffff + + union { + struct ipr_ioasa_vset vset; + struct ipr_ioasa_af_dasd dasd; + struct ipr_ioasa_gpdd gpdd; + struct ipr_ioasa_raw raw; + } u; +}__attribute__((packed, aligned (4))); + +struct ipr_mode_parm_hdr { + u8 length; + u8 medium_type; + u8 device_spec_parms; + u8 block_desc_len; +}__attribute__((packed)); + +struct ipr_mode_pages { + struct ipr_mode_parm_hdr hdr; + u8 data[255 - sizeof(struct ipr_mode_parm_hdr)]; +}__attribute__((packed)); + +struct ipr_mode_page_hdr { + u8 ps_page_code; +#define IPR_MODE_PAGE_PS 0x80 +#define IPR_GET_MODE_PAGE_CODE(hdr) ((hdr)->ps_page_code & 0x3F) + u8 page_length; +}__attribute__ ((packed)); + +struct ipr_dev_bus_entry { + struct ipr_res_addr res_addr; + u8 flags; +#define IPR_SCSI_ATTR_ENABLE_QAS 0x80 +#define IPR_SCSI_ATTR_DISABLE_QAS 0x40 +#define IPR_SCSI_ATTR_QAS_MASK 0xC0 +#define IPR_SCSI_ATTR_ENABLE_TM 0x20 +#define IPR_SCSI_ATTR_NO_TERM_PWR 0x10 +#define IPR_SCSI_ATTR_TM_SUPPORTED 0x08 +#define IPR_SCSI_ATTR_LVD_TO_SE_NOT_ALLOWED 0x04 + + u8 scsi_id; + u8 bus_width; + u8 extended_reset_delay; +#define IPR_EXTENDED_RESET_DELAY 7 + + u32 max_xfer_rate; + + u8 spinup_delay; + u8 reserved3; + u16 reserved4; +}__attribute__((packed, aligned (4))); + +struct ipr_mode_page28 { + struct ipr_mode_page_hdr hdr; + u8 num_entries; + u8 entry_length; + struct ipr_dev_bus_entry bus[0]; +}__attribute__((packed)); + +struct ipr_ioa_vpd { + struct ipr_std_inq_data std_inq_data; + u8 ascii_part_num[12]; + u8 reserved[40]; + u8 ascii_plant_code[4]; +}__attribute__((packed)); + +struct ipr_inquiry_page3 { + u8 peri_qual_dev_type; + u8 page_code; + u8 reserved1; + u8 page_length; + u8 ascii_len; + u8 reserved2[3]; + u8 load_id[4]; + u8 major_release; + u8 card_type; + u8 minor_release[2]; + u8 ptf_number[4]; + u8 patch_number[4]; +}__attribute__((packed)); + +struct ipr_hostrcb_device_data_entry { + struct ipr_std_inq_vpids dev_vpids; + u8 dev_sn[IPR_SERIAL_NUM_LEN]; + struct ipr_res_addr dev_res_addr; + struct ipr_std_inq_vpids new_dev_vpids; + u8 new_dev_sn[IPR_SERIAL_NUM_LEN]; + struct ipr_std_inq_vpids ioa_last_with_dev_vpids; + u8 ioa_last_with_dev_sn[IPR_SERIAL_NUM_LEN]; + struct ipr_std_inq_vpids cfc_last_with_dev_vpids; + u8 cfc_last_with_dev_sn[IPR_SERIAL_NUM_LEN]; + u32 ioa_data[5]; +}__attribute__((packed, aligned (4))); + +struct ipr_hostrcb_array_data_entry { + struct ipr_std_inq_vpids vpids; + u8 serial_num[IPR_SERIAL_NUM_LEN]; + struct ipr_res_addr expected_dev_res_addr; + struct ipr_res_addr dev_res_addr; +}__attribute__((packed, aligned (4))); + +struct ipr_hostrcb_type_ff_error { + u32 ioa_data[246]; +}__attribute__((packed, aligned (4))); + +struct ipr_hostrcb_type_01_error { + u32 seek_counter; + u32 read_counter; + u8 sense_data[32]; + u32 ioa_data[236]; +}__attribute__((packed, aligned (4))); + +struct ipr_hostrcb_type_02_error { + struct ipr_std_inq_vpids ioa_vpids; + u8 ioa_sn[IPR_SERIAL_NUM_LEN]; + struct ipr_std_inq_vpids cfc_vpids; + u8 cfc_sn[IPR_SERIAL_NUM_LEN]; + struct ipr_std_inq_vpids ioa_last_attached_to_cfc_vpids; + u8 ioa_last_attached_to_cfc_sn[IPR_SERIAL_NUM_LEN]; + struct ipr_std_inq_vpids cfc_last_attached_to_ioa_vpids; + u8 cfc_last_attached_to_ioa_sn[IPR_SERIAL_NUM_LEN]; + u32 ioa_data[3]; + u8 reserved[844]; +}__attribute__((packed, aligned (4))); + +struct ipr_hostrcb_type_03_error { + struct ipr_std_inq_vpids ioa_vpids; + u8 ioa_sn[IPR_SERIAL_NUM_LEN]; + struct ipr_std_inq_vpids cfc_vpids; + u8 cfc_sn[IPR_SERIAL_NUM_LEN]; + u32 errors_detected; + u32 errors_logged; + u8 ioa_data[12]; + struct ipr_hostrcb_device_data_entry dev_entry[3]; + u8 reserved[444]; +}__attribute__((packed, aligned (4))); + +struct ipr_hostrcb_type_04_error { + struct ipr_std_inq_vpids ioa_vpids; + u8 ioa_sn[IPR_SERIAL_NUM_LEN]; + struct ipr_std_inq_vpids cfc_vpids; + u8 cfc_sn[IPR_SERIAL_NUM_LEN]; + u8 ioa_data[12]; + struct ipr_hostrcb_array_data_entry array_member[10]; + u32 exposed_mode_adn; + u32 array_id; + struct ipr_std_inq_vpids incomp_dev_vpids; + u8 incomp_dev_sn[IPR_SERIAL_NUM_LEN]; + u32 ioa_data2; + struct ipr_hostrcb_array_data_entry array_member2[8]; + struct ipr_res_addr last_func_vset_res_addr; + u8 vset_serial_num[IPR_SERIAL_NUM_LEN]; + u8 protection_level[8]; + u8 reserved[124]; +}__attribute__((packed, aligned (4))); + +struct ipr_hostrcb_error { + u32 failing_dev_ioasc; + struct ipr_res_addr failing_dev_res_addr; + u32 failing_dev_res_handle; + u32 prc; + union { + struct ipr_hostrcb_type_ff_error type_ff_error; + struct ipr_hostrcb_type_01_error type_01_error; + struct ipr_hostrcb_type_02_error type_02_error; + struct ipr_hostrcb_type_03_error type_03_error; + struct ipr_hostrcb_type_04_error type_04_error; + } u; +}__attribute__((packed, aligned (4))); + +struct ipr_hostrcb_raw { + u32 data[sizeof(struct ipr_hostrcb_error)/sizeof(u32)]; +}__attribute__((packed, aligned (4))); + +struct ipr_hcam { + u8 op_code; +#define IPR_HOST_RCB_OP_CODE_CONFIG_CHANGE 0xE1 +#define IPR_HOST_RCB_OP_CODE_LOG_DATA 0xE2 + + u8 notify_type; +#define IPR_HOST_RCB_NOTIF_TYPE_EXISTING_CHANGED 0x00 +#define IPR_HOST_RCB_NOTIF_TYPE_NEW_ENTRY 0x01 +#define IPR_HOST_RCB_NOTIF_TYPE_REM_ENTRY 0x02 +#define IPR_HOST_RCB_NOTIF_TYPE_ERROR_LOG_ENTRY 0x10 +#define IPR_HOST_RCB_NOTIF_TYPE_INFORMATION_ENTRY 0x11 + + u8 notifications_lost; +#define IPR_HOST_RCB_NO_NOTIFICATIONS_LOST 0 +#define IPR_HOST_RCB_NOTIFICATIONS_LOST 0x80 + + u8 flags; +#define IPR_HOSTRCB_INTERNAL_OPER 0x80 +#define IPR_HOSTRCB_ERR_RESP_SENT 0x40 + + u8 overlay_id; +#define IPR_HOST_RCB_OVERLAY_ID_1 0x01 +#define IPR_HOST_RCB_OVERLAY_ID_2 0x02 +#define IPR_HOST_RCB_OVERLAY_ID_3 0x03 +#define IPR_HOST_RCB_OVERLAY_ID_4 0x04 +#define IPR_HOST_RCB_OVERLAY_ID_6 0x06 +#define IPR_HOST_RCB_OVERLAY_ID_DEFAULT 0xFF + + u8 reserved1[3]; + u32 ilid; + u32 time_since_last_ioa_reset; + u32 reserved2; + u32 length; + + union { + struct ipr_hostrcb_error error; + struct ipr_hostrcb_cfg_ch_not ccn; + struct ipr_hostrcb_raw raw; + } u; +}__attribute__((packed, aligned (4))); + +struct ipr_hostrcb { + struct ipr_hcam hcam; + u32 hostrcb_dma; + struct list_head queue; +}; + +/* IPR smart dump table structures */ +struct ipr_sdt_entry { + u32 bar_str_offset; + u32 end_offset; + u8 entry_byte; + u8 reserved[3]; + + u8 flags; +#define IPR_SDT_ENDIAN 0x80 +#define IPR_SDT_VALID_ENTRY 0x20 + + u8 resv; + u16 priority; +}__attribute__((packed, aligned (4))); + +struct ipr_sdt_header { + u32 state; + u32 num_entries; + u32 num_entries_used; + u32 dump_size; +}__attribute__((packed, aligned (4))); + +struct ipr_sdt { + struct ipr_sdt_header hdr; + struct ipr_sdt_entry entry[IPR_NUM_SDT_ENTRIES]; +}__attribute__((packed, aligned (4))); + +struct ipr_uc_sdt { + struct ipr_sdt_header hdr; + struct ipr_sdt_entry entry[1]; +}__attribute__((packed, aligned (4))); + +/* + * Driver types + */ +struct ipr_bus_attributes { + u8 bus; + u8 qas_enabled; + u8 bus_width; + u8 reserved; + u32 max_xfer_rate; +}; + +struct ipr_resource_entry { + struct ipr_config_table_entry cfgte; + u8 needs_sync_complete:1; + u8 in_erp:1; + u8 add_to_ml:1; + u8 del_from_ml:1; + u8 resetting_device:1; + u8 tcq_active:1; + + int qdepth; + struct scsi_device *sdev; + struct list_head queue; +}; + +struct ipr_resource_hdr { + u16 num_entries; + u16 reserved; +}; + +struct ipr_resource_table { + struct ipr_resource_hdr hdr; + struct ipr_resource_entry dev[IPR_MAX_PHYSICAL_DEVS]; +}; + +struct ipr_misc_cbs { + struct ipr_ioa_vpd ioa_vpd; + struct ipr_inquiry_page3 page3_data; + struct ipr_mode_pages mode_pages; + struct ipr_supported_device supp_dev; +}; + +struct ipr_interrupts { + unsigned long set_interrupt_mask_reg; + unsigned long clr_interrupt_mask_reg; + unsigned long sense_interrupt_mask_reg; + unsigned long clr_interrupt_reg; + + unsigned long sense_interrupt_reg; + unsigned long ioarrin_reg; + unsigned long sense_uproc_interrupt_reg; + unsigned long set_uproc_interrupt_reg; + unsigned long clr_uproc_interrupt_reg; +}; + +struct ipr_chip_cfg_t { + u32 mailbox; + u8 cache_line_size; + struct ipr_interrupts regs; +}; + +enum ipr_shutdown_type { + IPR_SHUTDOWN_NORMAL = 0x00, + IPR_SHUTDOWN_PREPARE_FOR_NORMAL = 0x40, + IPR_SHUTDOWN_ABBREV = 0x80, + IPR_SHUTDOWN_NONE = 0x100 +}; + +struct ipr_trace_entry { + u32 time; + + u8 op_code; + u8 type; +#define IPR_TRACE_START 0x00 +#define IPR_TRACE_FINISH 0xff + u16 cmd_index; + + u32 res_handle; + union { + u32 ioasc; + u32 add_data; + u32 res_addr; + } u; +}; + +struct ipr_sglist { + u32 order; + u32 num_sg; + u32 buffer_len; + struct scatterlist scatterlist[1]; +}; + +enum ipr_sdt_state { + INACTIVE, + WAIT_FOR_DUMP, + GET_DUMP, + ABORT_DUMP, + DUMP_OBTAINED +}; + +/* Per-controller data */ +struct ipr_ioa_cfg { + char eye_catcher[8]; +#define IPR_EYECATCHER "iprcfg" + + struct list_head queue; + + u8 allow_interrupts:1; + u8 in_reset_reload:1; + u8 in_ioa_bringdown:1; + u8 ioa_unit_checked:1; + u8 ioa_is_dead:1; + u8 dump_taken:1; + u8 allow_cmds:1; + u8 allow_ml_add_del:1; + + u16 type; /* CCIN of the card */ + + u8 log_level; +#define IPR_MAX_LOG_LEVEL 4 +#define IPR_DEFAULT_LOG_LEVEL 2 + +#define IPR_NUM_TRACE_INDEX_BITS 8 +#define IPR_NUM_TRACE_ENTRIES (1 << IPR_NUM_TRACE_INDEX_BITS) +#define IPR_TRACE_SIZE (sizeof(struct ipr_trace_entry) * IPR_NUM_TRACE_ENTRIES) + char trace_start[8]; +#define IPR_TRACE_START_LABEL "trace" + struct ipr_trace_entry *trace; + u32 trace_index:IPR_NUM_TRACE_INDEX_BITS; + + /* + * Queue for free command blocks + */ + char ipr_free_label[8]; +#define IPR_FREEQ_LABEL "free-q" + struct list_head free_q; + + /* + * Queue for command blocks outstanding to the adapter + */ + char ipr_pending_label[8]; +#define IPR_PENDQ_LABEL "pend-q" + struct list_head pending_q; + + char cfg_table_start[8]; +#define IPR_CFG_TBL_START "cfg" + struct ipr_config_table *cfg_table; + u32 cfg_table_dma; + + char resource_table_label[8]; +#define IPR_RES_TABLE_LABEL "res_tbl" + struct ipr_resource_entry *res_entries; + struct list_head free_res_q; + struct list_head used_res_q; + + char ipr_hcam_label[8]; +#define IPR_HCAM_LABEL "hcams" + struct ipr_hostrcb *hostrcb[IPR_NUM_HCAMS]; + u32 hostrcb_dma[IPR_NUM_HCAMS]; + struct list_head hostrcb_free_q; + struct list_head hostrcb_pending_q; + + u32 *host_rrq; + u32 host_rrq_dma; +#define IPR_HRRQ_REQ_RESP_HANDLE_MASK 0xfffffffc +#define IPR_HRRQ_RESP_BIT_SET 0x00000002 +#define IPR_HRRQ_TOGGLE_BIT 0x00000001 +#define IPR_HRRQ_REQ_RESP_HANDLE_SHIFT 2 + volatile u32 *hrrq_start; + volatile u32 *hrrq_end; + volatile u32 *hrrq_curr; + volatile u32 toggle_bit; + + struct ipr_bus_attributes bus_attr[IPR_MAX_NUM_BUSES]; + + const struct ipr_chip_cfg_t *chip_cfg; + + unsigned long hdw_dma_regs; /* iomapped PCI memory space */ + unsigned long hdw_dma_regs_pci; /* raw PCI memory space */ + unsigned long ioa_mailbox; + struct ipr_interrupts regs; + + u32 pci_cfg_buf[64]; + u16 saved_pcix_cmd_reg; + u16 reset_retries; + + u32 errors_logged; + + struct Scsi_Host *host; + struct pci_dev *pdev; + struct ipr_sglist *ucode_sglist; + struct ipr_mode_pages *saved_mode_pages; + u8 saved_mode_page_len; + + struct work_struct work_q; + + wait_queue_head_t reset_wait_q; + + struct ipr_dump *dump; + enum ipr_sdt_state sdt_state; + + struct ipr_misc_cbs *vpd_cbs; + u32 vpd_cbs_dma; + + struct pci_pool *ipr_cmd_pool; + + struct ipr_cmnd *reset_cmd; + + char ipr_cmd_label[8]; +#define IPR_CMD_LABEL "ipr_cmnd" + struct ipr_cmnd *ipr_cmnd_list[IPR_NUM_CMD_BLKS]; + u32 ipr_cmnd_list_dma[IPR_NUM_CMD_BLKS]; +}; + +struct ipr_cmnd { + struct ipr_ioarcb ioarcb; + struct ipr_ioasa ioasa; + struct ipr_ioadl_desc ioadl[IPR_NUM_IOADL_ENTRIES]; + struct list_head queue; + struct scsi_cmnd *scsi_cmd; + struct completion completion; + struct timer_list timer; + void (*done) (struct ipr_cmnd *); + int (*job_step) (struct ipr_cmnd *); + u16 cmd_index; + u8 sense_buffer[SCSI_SENSE_BUFFERSIZE]; + dma_addr_t sense_buffer_dma; + unsigned short dma_use_sg; + dma_addr_t dma_handle; + union { + enum ipr_shutdown_type shutdown_type; + struct ipr_hostrcb *hostrcb; + unsigned long time_left; + unsigned long scratch; + struct ipr_resource_entry *res; + struct ipr_cmnd *sibling; + struct scsi_device *sdev; + } u; + + struct ipr_ioa_cfg *ioa_cfg; +}; + +struct ipr_ses_table_entry { + char product_id[17]; + char compare_product_id_byte[17]; + u32 max_bus_speed_limit; /* MB/sec limit for this backplane */ +}; + +struct ipr_dump_header { + u32 eye_catcher; +#define IPR_DUMP_EYE_CATCHER 0xC5D4E3F2 + u32 len; + u32 num_entries; + u32 first_entry_offset; + u32 status; +#define IPR_DUMP_STATUS_SUCCESS 0 +#define IPR_DUMP_STATUS_QUAL_SUCCESS 2 +#define IPR_DUMP_STATUS_FAILED 0xffffffff + u32 os; +#define IPR_DUMP_OS_LINUX 0x4C4E5558 + u32 driver_name; +#define IPR_DUMP_DRIVER_NAME 0x49505232 +}__attribute__((packed, aligned (4))); + +struct ipr_dump_entry_header { + u32 eye_catcher; +#define IPR_DUMP_EYE_CATCHER 0xC5D4E3F2 + u32 len; + u32 num_elems; + u32 offset; + u32 data_type; +#define IPR_DUMP_DATA_TYPE_ASCII 0x41534349 +#define IPR_DUMP_DATA_TYPE_BINARY 0x42494E41 + u32 id; +#define IPR_DUMP_IOA_DUMP_ID 0x494F4131 +#define IPR_DUMP_LOCATION_ID 0x4C4F4341 +#define IPR_DUMP_TRACE_ID 0x54524143 +#define IPR_DUMP_DRIVER_VERSION_ID 0x44525652 +#define IPR_DUMP_DRIVER_TYPE_ID 0x54595045 +#define IPR_DUMP_IOA_CTRL_BLK 0x494F4342 +#define IPR_DUMP_PEND_OPS 0x414F5053 + u32 status; +}__attribute__((packed, aligned (4))); + +struct ipr_dump_location_entry { + struct ipr_dump_entry_header hdr; + u8 location[BUS_ID_SIZE]; +}__attribute__((packed)); + +struct ipr_dump_trace_entry { + struct ipr_dump_entry_header hdr; + u32 trace[IPR_TRACE_SIZE / sizeof(u32)]; +}__attribute__((packed, aligned (4))); + +struct ipr_dump_version_entry { + struct ipr_dump_entry_header hdr; + u8 version[sizeof(IPR_DRIVER_VERSION)]; +}; + +struct ipr_dump_ioa_type_entry { + struct ipr_dump_entry_header hdr; + u32 type; + u32 fw_version; +}; + +struct ipr_driver_dump { + struct ipr_dump_header hdr; + struct ipr_dump_version_entry version_entry; + struct ipr_dump_location_entry location_entry; + struct ipr_dump_ioa_type_entry ioa_type_entry; + struct ipr_dump_trace_entry trace_entry; +}__attribute__((packed)); + +struct ipr_ioa_dump { + struct ipr_dump_entry_header hdr; + struct ipr_sdt sdt; + u32 *ioa_data[IPR_MAX_NUM_DUMP_PAGES]; + u32 reserved; + u32 next_page_index; + u32 page_offset; + u32 format; +#define IPR_SDT_FMT2 2 +#define IPR_SDT_UNKNOWN 3 +}__attribute__((packed, aligned (4))); + +struct ipr_dump { + struct kobject kobj; + struct ipr_ioa_cfg *ioa_cfg; + struct ipr_driver_dump driver_dump; + struct ipr_ioa_dump ioa_dump; +}; + +struct ipr_error_table_t { + u32 ioasc; + int log_ioasa; + int log_hcam; + char *error; +}; + +struct ipr_software_inq_lid_info { + u32 load_id; + u32 timestamp[3]; +}__attribute__((packed, aligned (4))); + +struct ipr_ucode_image_header { + u32 header_length; + u32 lid_table_offset; + u8 major_release; + u8 card_type; + u8 minor_release[2]; + u8 reserved[20]; + char eyecatcher[16]; + u32 num_lids; + struct ipr_software_inq_lid_info lid[1]; +}__attribute__((packed, aligned (4))); + +/* + * Macros + */ +#if IPR_DEBUG +#define IPR_DBG_CMD(CMD) do { CMD; } while (0) +#else +#define IPR_DBG_CMD(CMD) +#endif + +#define ipr_breakpoint_data KERN_ERR IPR_NAME\ +": %s: %s: Line: %d ioa_cfg: %p\n", __FILE__, \ +__FUNCTION__, __LINE__, ioa_cfg + +#if defined(CONFIG_KDB) && !defined(CONFIG_PPC_ISERIES) +#define ipr_breakpoint {printk(ipr_breakpoint_data); KDB_ENTER();} +#define ipr_breakpoint_or_die {printk(ipr_breakpoint_data); KDB_ENTER();} +#else +#define ipr_breakpoint +#define ipr_breakpoint_or_die panic(ipr_breakpoint_data) +#endif + +#ifdef CONFIG_SCSI_IPR_TRACE +#define ipr_create_trace_file(kobj, attr) sysfs_create_bin_file(kobj, attr) +#define ipr_remove_trace_file(kobj, attr) sysfs_remove_bin_file(kobj, attr) +#else +#define ipr_create_trace_file(kobj, attr) 0 +#define ipr_remove_trace_file(kobj, attr) do { } while(0) +#endif + +#ifdef CONFIG_SCSI_IPR_DUMP +#define ipr_create_dump_file(kobj, attr) sysfs_create_bin_file(kobj, attr) +#define ipr_remove_dump_file(kobj, attr) sysfs_remove_bin_file(kobj, attr) +#else +#define ipr_create_dump_file(kobj, attr) 0 +#define ipr_remove_dump_file(kobj, attr) do { } while(0) +#endif + +/* + * Error logging macros + */ +#define ipr_err(...) printk(KERN_ERR IPR_NAME ": "__VA_ARGS__) +#define ipr_info(...) printk(KERN_INFO IPR_NAME ": "__VA_ARGS__) +#define ipr_crit(...) printk(KERN_CRIT IPR_NAME ": "__VA_ARGS__) +#define ipr_warn(...) printk(KERN_WARNING IPR_NAME": "__VA_ARGS__) +#define ipr_dbg(...) IPR_DBG_CMD(printk(KERN_INFO IPR_NAME ": "__VA_ARGS__)) + +#define ipr_sdev_printk(level, sdev, fmt, ...) \ + printk(level IPR_NAME ": %d:%d:%d:%d: " fmt, sdev->host->host_no, \ + sdev->channel, sdev->id, sdev->lun, ##__VA_ARGS__) + +#define ipr_sdev_err(sdev, fmt, ...) \ + ipr_sdev_printk(KERN_ERR, sdev, fmt, ##__VA_ARGS__) + +#define ipr_sdev_info(sdev, fmt, ...) \ + ipr_sdev_printk(KERN_INFO, sdev, fmt, ##__VA_ARGS__) + +#define ipr_sdev_dbg(sdev, fmt, ...) \ + IPR_DBG_CMD(ipr_sdev_printk(KERN_INFO, sdev, fmt, ##__VA_ARGS__)) + +#define ipr_res_printk(level, ioa_cfg, res, fmt, ...) \ + printk(level IPR_NAME ": %d:%d:%d:%d: " fmt, ioa_cfg->host->host_no, \ + res.bus, res.target, res.lun, ##__VA_ARGS__) + +#define ipr_res_err(ioa_cfg, res, fmt, ...) \ + ipr_res_printk(KERN_ERR, ioa_cfg, res, fmt, ##__VA_ARGS__) +#define ipr_res_dbg(ioa_cfg, res, fmt, ...) \ + IPR_DBG_CMD(ipr_res_printk(KERN_INFO, ioa_cfg, res, fmt, ##__VA_ARGS__)) + +#define ipr_trace ipr_dbg("%s: %s: Line: %d\n",\ + __FILE__, __FUNCTION__, __LINE__) + +#if IPR_DBG_TRACE +#define ENTER printk(KERN_INFO IPR_NAME": Entering %s\n", __FUNCTION__) +#define LEAVE printk(KERN_INFO IPR_NAME": Leaving %s\n", __FUNCTION__) +#else +#define ENTER +#define LEAVE +#endif + +#define ipr_err_separator \ +ipr_err("----------------------------------------------------------\n") + + +/* + * Inlines + */ + +/** + * ipr_is_ioa_resource - Determine if a resource is the IOA + * @res: resource entry struct + * + * Return value: + * 1 if IOA / 0 if not IOA + **/ +static inline int ipr_is_ioa_resource(struct ipr_resource_entry *res) +{ + return (res->cfgte.flags & IPR_IS_IOA_RESOURCE) ? 1 : 0; +} + +/** + * ipr_is_af_dasd_device - Determine if a resource is an AF DASD + * @res: resource entry struct + * + * Return value: + * 1 if AF DASD / 0 if not AF DASD + **/ +static inline int ipr_is_af_dasd_device(struct ipr_resource_entry *res) +{ + if (IPR_IS_DASD_DEVICE(res->cfgte.std_inq_data) && + !ipr_is_ioa_resource(res) && + IPR_RES_SUBTYPE(res) == IPR_SUBTYPE_AF_DASD) + return 1; + else + return 0; +} + +/** + * ipr_is_vset_device - Determine if a resource is a VSET + * @res: resource entry struct + * + * Return value: + * 1 if VSET / 0 if not VSET + **/ +static inline int ipr_is_vset_device(struct ipr_resource_entry *res) +{ + if (IPR_IS_DASD_DEVICE(res->cfgte.std_inq_data) && + !ipr_is_ioa_resource(res) && + IPR_RES_SUBTYPE(res) == IPR_SUBTYPE_VOLUME_SET) + return 1; + else + return 0; +} + +/** + * ipr_is_gscsi - Determine if a resource is a generic scsi resource + * @res: resource entry struct + * + * Return value: + * 1 if GSCSI / 0 if not GSCSI + **/ +static inline int ipr_is_gscsi(struct ipr_resource_entry *res) +{ + if (!ipr_is_ioa_resource(res) && + IPR_RES_SUBTYPE(res) == IPR_SUBTYPE_GENERIC_SCSI) + return 1; + else + return 0; +} + +/** + * ipr_is_device - Determine if resource address is that of a device + * @res_addr: resource address struct + * + * Return value: + * 1 if AF / 0 if not AF + **/ +static inline int ipr_is_device(struct ipr_res_addr *res_addr) +{ + if ((res_addr->bus < IPR_MAX_NUM_BUSES) && + (res_addr->target < IPR_MAX_NUM_TARGETS_PER_BUS)) + return 1; + + return 0; +} + +/** + * ipr_sdt_is_fmt2 - Determine if a SDT address is in format 2 + * @sdt_word: SDT address + * + * Return value: + * 1 if format 2 / 0 if not + **/ +static inline int ipr_sdt_is_fmt2(u32 sdt_word) +{ + u32 bar_sel = IPR_GET_FMT2_BAR_SEL(sdt_word); + + switch (bar_sel) { + case IPR_SDT_FMT2_BAR0_SEL: + case IPR_SDT_FMT2_BAR1_SEL: + case IPR_SDT_FMT2_BAR2_SEL: + case IPR_SDT_FMT2_BAR3_SEL: + case IPR_SDT_FMT2_BAR4_SEL: + case IPR_SDT_FMT2_BAR5_SEL: + case IPR_SDT_FMT2_EXP_ROM_SEL: + return 1; + }; + + return 0; +} + +#endif diff --git a/drivers/scsi/pcmcia/qlogic_core.c b/drivers/scsi/pcmcia/qlogic_core.c deleted file mode 100644 index 78abe22b1..000000000 --- a/drivers/scsi/pcmcia/qlogic_core.c +++ /dev/null @@ -1,2 +0,0 @@ -#define PCMCIA 1 -#include "qlogicfas.c" diff --git a/drivers/scsi/pcmcia/sym53c500_cs.c b/drivers/scsi/pcmcia/sym53c500_cs.c new file mode 100644 index 000000000..4277db3b6 --- /dev/null +++ b/drivers/scsi/pcmcia/sym53c500_cs.c @@ -0,0 +1,1042 @@ +/* +* sym53c500_cs.c Bob Tracy (rct@frus.com) +* +* A rewrite of the pcmcia-cs add-on driver for newer (circa 1997) +* New Media Bus Toaster PCMCIA SCSI cards using the Symbios Logic +* 53c500 controller: intended for use with 2.6 and later kernels. +* The pcmcia-cs add-on version of this driver is not supported +* beyond 2.4. It consisted of three files with history/copyright +* information as follows: +* +* SYM53C500.h +* Bob Tracy (rct@frus.com) +* Original by Tom Corner (tcorner@via.at). +* Adapted from NCR53c406a.h which is Copyrighted (C) 1994 +* Normunds Saumanis (normunds@rx.tech.swh.lv) +* +* SYM53C500.c +* Bob Tracy (rct@frus.com) +* Original driver by Tom Corner (tcorner@via.at) was adapted +* from NCR53c406a.c which is Copyrighted (C) 1994, 1995, 1996 +* Normunds Saumanis (normunds@fi.ibm.com) +* +* sym53c500.c +* Bob Tracy (rct@frus.com) +* Original by Tom Corner (tcorner@via.at) was adapted from a +* driver for the Qlogic SCSI card written by +* David Hinds (dhinds@allegro.stanford.edu). +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2, or (at your option) any +* later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +*/ + +#define SYM53C500_DEBUG 0 +#define VERBOSE_SYM53C500_DEBUG 0 + +/* +* Set this to 0 if you encounter kernel lockups while transferring +* data in PIO mode. Note this can be changed via "sysfs". +*/ +#define USE_FAST_PIO 1 + +/* =============== End of user configurable parameters ============== */ + +#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 + +/* ================================================================== */ + +#ifdef PCMCIA_DEBUG +static int pc_debug = PCMCIA_DEBUG; +module_param(pc_debug, int, 0); +#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args) +static char *version = +"sym53c500_cs.c 0.9b 2004/05/10 (Bob Tracy)"; +#else +#define DEBUG(n, args...) +#endif + +/* ================================================================== */ + +/* Parameters that can be set with 'insmod' */ + +/* Bit map of interrupts to choose from */ +static unsigned int irq_mask = 0xdeb8; /* 3, 6, 7, 9-12, 14, 15 */ +static int irq_list[4] = { -1 }; +static int num_irqs = 1; + +module_param(irq_mask, int, 0); +MODULE_PARM_DESC(irq_mask, "IRQ mask bits (default: 0xdeb8)"); +module_param_array(irq_list, int, num_irqs, 0); +MODULE_PARM_DESC(irq_list, "Comma-separated list of up to 4 IRQs to try (default: auto select)."); + +/* ================================================================== */ + +#define SYNC_MODE 0 /* Synchronous transfer mode */ + +/* Default configuration */ +#define C1_IMG 0x07 /* ID=7 */ +#define C2_IMG 0x48 /* FE SCSI2 */ +#define C3_IMG 0x20 /* CDB */ +#define C4_IMG 0x04 /* ANE */ +#define C5_IMG 0xa4 /* ? changed from b6= AA PI SIE POL */ +#define C7_IMG 0x80 /* added for SYM53C500 t. corner */ + +/* Hardware Registers: offsets from io_port (base) */ + +/* Control Register Set 0 */ +#define TC_LSB 0x00 /* transfer counter lsb */ +#define TC_MSB 0x01 /* transfer counter msb */ +#define SCSI_FIFO 0x02 /* scsi fifo register */ +#define CMD_REG 0x03 /* command register */ +#define STAT_REG 0x04 /* status register */ +#define DEST_ID 0x04 /* selection/reselection bus id */ +#define INT_REG 0x05 /* interrupt status register */ +#define SRTIMOUT 0x05 /* select/reselect timeout reg */ +#define SEQ_REG 0x06 /* sequence step register */ +#define SYNCPRD 0x06 /* synchronous transfer period */ +#define FIFO_FLAGS 0x07 /* indicates # of bytes in fifo */ +#define SYNCOFF 0x07 /* synchronous offset register */ +#define CONFIG1 0x08 /* configuration register */ +#define CLKCONV 0x09 /* clock conversion register */ +/* #define TESTREG 0x0A */ /* test mode register */ +#define CONFIG2 0x0B /* configuration 2 register */ +#define CONFIG3 0x0C /* configuration 3 register */ +#define CONFIG4 0x0D /* configuration 4 register */ +#define TC_HIGH 0x0E /* transfer counter high */ +/* #define FIFO_BOTTOM 0x0F */ /* reserve FIFO byte register */ + +/* Control Register Set 1 */ +/* #define JUMPER_SENSE 0x00 */ /* jumper sense port reg (r/w) */ +/* #define SRAM_PTR 0x01 */ /* SRAM address pointer reg (r/w) */ +/* #define SRAM_DATA 0x02 */ /* SRAM data register (r/w) */ +#define PIO_FIFO 0x04 /* PIO FIFO registers (r/w) */ +/* #define PIO_FIFO1 0x05 */ /* */ +/* #define PIO_FIFO2 0x06 */ /* */ +/* #define PIO_FIFO3 0x07 */ /* */ +#define PIO_STATUS 0x08 /* PIO status (r/w) */ +/* #define ATA_CMD 0x09 */ /* ATA command/status reg (r/w) */ +/* #define ATA_ERR 0x0A */ /* ATA features/error reg (r/w) */ +#define PIO_FLAG 0x0B /* PIO flag interrupt enable (r/w) */ +#define CONFIG5 0x09 /* configuration 5 register */ +/* #define SIGNATURE 0x0E */ /* signature register (r) */ +/* #define CONFIG6 0x0F */ /* configuration 6 register (r) */ +#define CONFIG7 0x0d + +/* select register set 0 */ +#define REG0(x) (outb(C4_IMG, (x) + CONFIG4)) +/* select register set 1 */ +#define REG1(x) outb(C7_IMG, (x) + CONFIG7); outb(C5_IMG, (x) + CONFIG5) + +#if SYM53C500_DEBUG +#define DEB(x) x +#else +#define DEB(x) +#endif + +#if VERBOSE_SYM53C500_DEBUG +#define VDEB(x) x +#else +#define VDEB(x) +#endif + +#define LOAD_DMA_COUNT(x, count) \ + outb(count & 0xff, (x) + TC_LSB); \ + outb((count >> 8) & 0xff, (x) + TC_MSB); \ + outb((count >> 16) & 0xff, (x) + TC_HIGH); + +/* Chip commands */ +#define DMA_OP 0x80 + +#define SCSI_NOP 0x00 +#define FLUSH_FIFO 0x01 +#define CHIP_RESET 0x02 +#define SCSI_RESET 0x03 +#define RESELECT 0x40 +#define SELECT_NO_ATN 0x41 +#define SELECT_ATN 0x42 +#define SELECT_ATN_STOP 0x43 +#define ENABLE_SEL 0x44 +#define DISABLE_SEL 0x45 +#define SELECT_ATN3 0x46 +#define RESELECT3 0x47 +#define TRANSFER_INFO 0x10 +#define INIT_CMD_COMPLETE 0x11 +#define MSG_ACCEPT 0x12 +#define TRANSFER_PAD 0x18 +#define SET_ATN 0x1a +#define RESET_ATN 0x1b +#define SEND_MSG 0x20 +#define SEND_STATUS 0x21 +#define SEND_DATA 0x22 +#define DISCONN_SEQ 0x23 +#define TERMINATE_SEQ 0x24 +#define TARG_CMD_COMPLETE 0x25 +#define DISCONN 0x27 +#define RECV_MSG 0x28 +#define RECV_CMD 0x29 +#define RECV_DATA 0x2a +#define RECV_CMD_SEQ 0x2b +#define TARGET_ABORT_DMA 0x04 + +/* ================================================================== */ + +struct scsi_info_t { + dev_link_t link; + dev_node_t node; + struct Scsi_Host *host; + unsigned short manf_id; +}; + +/* +* Repository for per-instance host data. +*/ +struct sym53c500_data { + struct scsi_cmnd *current_SC; + int fast_pio; +}; + +enum Phase { + idle, + data_out, + data_in, + command_ph, + status_ph, + message_out, + message_in +}; + +/* ================================================================== */ + +/* +* Global (within this module) variables other than +* sym53c500_driver_template (the scsi_host_template). +*/ +static dev_link_t *dev_list; +static dev_info_t dev_info = "sym53c500_cs"; + +/* ================================================================== */ + +static void +chip_init(int io_port) +{ + REG1(io_port); + outb(0x01, io_port + PIO_STATUS); + outb(0x00, io_port + PIO_FLAG); + + outb(C4_IMG, io_port + CONFIG4); /* REG0(io_port); */ + outb(C3_IMG, io_port + CONFIG3); + outb(C2_IMG, io_port + CONFIG2); + outb(C1_IMG, io_port + CONFIG1); + + outb(0x05, io_port + CLKCONV); /* clock conversion factor */ + outb(0x9C, io_port + SRTIMOUT); /* Selection timeout */ + outb(0x05, io_port + SYNCPRD); /* Synchronous transfer period */ + outb(SYNC_MODE, io_port + SYNCOFF); /* synchronous mode */ +} + +static void +SYM53C500_int_host_reset(int io_port) +{ + outb(C4_IMG, io_port + CONFIG4); /* REG0(io_port); */ + outb(CHIP_RESET, io_port + CMD_REG); + outb(SCSI_NOP, io_port + CMD_REG); /* required after reset */ + outb(SCSI_RESET, io_port + CMD_REG); + chip_init(io_port); +} + +static __inline__ int +SYM53C500_pio_read(int fast_pio, int base, unsigned char *request, unsigned int reqlen) +{ + int i; + int len; /* current scsi fifo size */ + + REG1(base); + while (reqlen) { + i = inb(base + PIO_STATUS); + /* VDEB(printk("pio_status=%x\n", i)); */ + if (i & 0x80) + return 0; + + switch (i & 0x1e) { + default: + case 0x10: /* fifo empty */ + len = 0; + break; + case 0x0: + len = 1; + break; + case 0x8: /* fifo 1/3 full */ + len = 42; + break; + case 0xc: /* fifo 2/3 full */ + len = 84; + break; + case 0xe: /* fifo full */ + len = 128; + break; + } + + if ((i & 0x40) && len == 0) { /* fifo empty and interrupt occurred */ + return 0; + } + + if (len) { + if (len > reqlen) + len = reqlen; + + if (fast_pio && len > 3) { + insl(base + PIO_FIFO, request, len >> 2); + request += len & 0xfc; + reqlen -= len & 0xfc; + } else { + while (len--) { + *request++ = inb(base + PIO_FIFO); + reqlen--; + } + } + } + } + return 0; +} + +static __inline__ int +SYM53C500_pio_write(int fast_pio, int base, unsigned char *request, unsigned int reqlen) +{ + int i = 0; + int len; /* current scsi fifo size */ + + REG1(base); + while (reqlen && !(i & 0x40)) { + i = inb(base + PIO_STATUS); + /* VDEB(printk("pio_status=%x\n", i)); */ + if (i & 0x80) /* error */ + return 0; + + switch (i & 0x1e) { + case 0x10: + len = 128; + break; + case 0x0: + len = 84; + break; + case 0x8: + len = 42; + break; + case 0xc: + len = 1; + break; + default: + case 0xe: + len = 0; + break; + } + + if (len) { + if (len > reqlen) + len = reqlen; + + if (fast_pio && len > 3) { + outsl(base + PIO_FIFO, request, len >> 2); + request += len & 0xfc; + reqlen -= len & 0xfc; + } else { + while (len--) { + outb(*request++, base + PIO_FIFO); + reqlen--; + } + } + } + } + return 0; +} + +static irqreturn_t +SYM53C500_intr(int irq, void *dev_id, struct pt_regs *regs) +{ + unsigned long flags; + struct Scsi_Host *dev = dev_id; + DEB(unsigned char fifo_size;) + DEB(unsigned char seq_reg;) + unsigned char status, int_reg; + unsigned char pio_status; + struct scatterlist *sglist; + unsigned int sgcount; + int port_base = dev->io_port; + struct sym53c500_data *data = + (struct sym53c500_data *)dev->hostdata; + struct scsi_cmnd *curSC = data->current_SC; + int fast_pio = data->fast_pio; + + spin_lock_irqsave(dev->host_lock, flags); + + VDEB(printk("SYM53C500_intr called\n")); + + REG1(port_base); + pio_status = inb(port_base + PIO_STATUS); + REG0(port_base); + status = inb(port_base + STAT_REG); + DEB(seq_reg = inb(port_base + SEQ_REG)); + int_reg = inb(port_base + INT_REG); + DEB(fifo_size = inb(port_base + FIFO_FLAGS) & 0x1f); + +#if SYM53C500_DEBUG + printk("status=%02x, seq_reg=%02x, int_reg=%02x, fifo_size=%02x", + status, seq_reg, int_reg, fifo_size); + printk(", pio=%02x\n", pio_status); +#endif /* SYM53C500_DEBUG */ + + if (int_reg & 0x80) { /* SCSI reset intr */ + DEB(printk("SYM53C500: reset intr received\n")); + curSC->result = DID_RESET << 16; + goto idle_out; + } + + if (pio_status & 0x80) { + printk("SYM53C500: Warning: PIO error!\n"); + curSC->result = DID_ERROR << 16; + goto idle_out; + } + + if (status & 0x20) { /* Parity error */ + printk("SYM53C500: Warning: parity error!\n"); + curSC->result = DID_PARITY << 16; + goto idle_out; + } + + if (status & 0x40) { /* Gross error */ + printk("SYM53C500: Warning: gross error!\n"); + curSC->result = DID_ERROR << 16; + goto idle_out; + } + + if (int_reg & 0x20) { /* Disconnect */ + DEB(printk("SYM53C500: disconnect intr received\n")); + if (curSC->SCp.phase != message_in) { /* Unexpected disconnect */ + curSC->result = DID_NO_CONNECT << 16; + } else { /* Command complete, return status and message */ + curSC->result = (curSC->SCp.Status & 0xff) + | ((curSC->SCp.Message & 0xff) << 8) | (DID_OK << 16); + } + goto idle_out; + } + + switch (status & 0x07) { /* scsi phase */ + case 0x00: /* DATA-OUT */ + if (int_reg & 0x10) { /* Target requesting info transfer */ + curSC->SCp.phase = data_out; + VDEB(printk("SYM53C500: Data-Out phase\n")); + outb(FLUSH_FIFO, port_base + CMD_REG); + LOAD_DMA_COUNT(port_base, curSC->request_bufflen); /* Max transfer size */ + outb(TRANSFER_INFO | DMA_OP, port_base + CMD_REG); + if (!curSC->use_sg) /* Don't use scatter-gather */ + SYM53C500_pio_write(fast_pio, port_base, curSC->request_buffer, curSC->request_bufflen); + else { /* use scatter-gather */ + sgcount = curSC->use_sg; + sglist = curSC->request_buffer; + while (sgcount--) { + SYM53C500_pio_write(fast_pio, port_base, page_address(sglist->page) + sglist->offset, sglist->length); + sglist++; + } + } + REG0(port_base); + } + break; + + case 0x01: /* DATA-IN */ + if (int_reg & 0x10) { /* Target requesting info transfer */ + curSC->SCp.phase = data_in; + VDEB(printk("SYM53C500: Data-In phase\n")); + outb(FLUSH_FIFO, port_base + CMD_REG); + LOAD_DMA_COUNT(port_base, curSC->request_bufflen); /* Max transfer size */ + outb(TRANSFER_INFO | DMA_OP, port_base + CMD_REG); + if (!curSC->use_sg) /* Don't use scatter-gather */ + SYM53C500_pio_read(fast_pio, port_base, curSC->request_buffer, curSC->request_bufflen); + else { /* Use scatter-gather */ + sgcount = curSC->use_sg; + sglist = curSC->request_buffer; + while (sgcount--) { + SYM53C500_pio_read(fast_pio, port_base, page_address(sglist->page) + sglist->offset, sglist->length); + sglist++; + } + } + REG0(port_base); + } + break; + + case 0x02: /* COMMAND */ + curSC->SCp.phase = command_ph; + printk("SYM53C500: Warning: Unknown interrupt occurred in command phase!\n"); + break; + + case 0x03: /* STATUS */ + curSC->SCp.phase = status_ph; + VDEB(printk("SYM53C500: Status phase\n")); + outb(FLUSH_FIFO, port_base + CMD_REG); + outb(INIT_CMD_COMPLETE, port_base + CMD_REG); + break; + + case 0x04: /* Reserved */ + case 0x05: /* Reserved */ + printk("SYM53C500: WARNING: Reserved phase!!!\n"); + break; + + case 0x06: /* MESSAGE-OUT */ + DEB(printk("SYM53C500: Message-Out phase\n")); + curSC->SCp.phase = message_out; + outb(SET_ATN, port_base + CMD_REG); /* Reject the message */ + outb(MSG_ACCEPT, port_base + CMD_REG); + break; + + case 0x07: /* MESSAGE-IN */ + VDEB(printk("SYM53C500: Message-In phase\n")); + curSC->SCp.phase = message_in; + + curSC->SCp.Status = inb(port_base + SCSI_FIFO); + curSC->SCp.Message = inb(port_base + SCSI_FIFO); + + VDEB(printk("SCSI FIFO size=%d\n", inb(port_base + FIFO_FLAGS) & 0x1f)); + DEB(printk("Status = %02x Message = %02x\n", curSC->SCp.Status, curSC->SCp.Message)); + + if (curSC->SCp.Message == SAVE_POINTERS || curSC->SCp.Message == DISCONNECT) { + outb(SET_ATN, port_base + CMD_REG); /* Reject message */ + DEB(printk("Discarding SAVE_POINTERS message\n")); + } + outb(MSG_ACCEPT, port_base + CMD_REG); + break; + } +out: + spin_unlock_irqrestore(dev->host_lock, flags); + return IRQ_HANDLED; + +idle_out: + curSC->SCp.phase = idle; + curSC->scsi_done(curSC); + goto out; +} + +static void +SYM53C500_release(dev_link_t *link) +{ + struct scsi_info_t *info = link->priv; + struct Scsi_Host *shost = info->host; + + DEBUG(0, "SYM53C500_release(0x%p)\n", link); + + /* + * Do this before releasing/freeing resources. + */ + scsi_remove_host(shost); + + /* + * Interrupts getting hosed on card removal. Try + * the following code, mostly from qlogicfas.c. + */ + if (shost->irq) + free_irq(shost->irq, shost); + if (shost->dma_channel != 0xff) + free_dma(shost->dma_channel); + if (shost->io_port && shost->n_io_port) + release_region(shost->io_port, shost->n_io_port); + + link->dev = NULL; + + pcmcia_release_configuration(link->handle); + pcmcia_release_io(link->handle, &link->io); + pcmcia_release_irq(link->handle, &link->irq); + + link->state &= ~DEV_CONFIG; + + scsi_host_put(shost); +} /* SYM53C500_release */ + +static const char* +SYM53C500_info(struct Scsi_Host *SChost) +{ + static char info_msg[256]; + struct sym53c500_data *data = + (struct sym53c500_data *)SChost->hostdata; + + DEB(printk("SYM53C500_info called\n")); + (void)snprintf(info_msg, sizeof(info_msg), + "SYM53C500 at 0x%lx, IRQ %d, %s PIO mode.", + SChost->io_port, SChost->irq, data->fast_pio ? "fast" : "slow"); + return (info_msg); +} + +static int +SYM53C500_queue(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *)) +{ + int i; + int port_base = SCpnt->device->host->io_port; + struct sym53c500_data *data = + (struct sym53c500_data *)SCpnt->device->host->hostdata; + + VDEB(printk("SYM53C500_queue called\n")); + + DEB(printk("cmd=%02x, cmd_len=%02x, target=%02x, lun=%02x, bufflen=%d\n", + SCpnt->cmnd[0], SCpnt->cmd_len, SCpnt->device->id, + SCpnt->device->lun, SCpnt->request_bufflen)); + + VDEB(for (i = 0; i < SCpnt->cmd_len; i++) + printk("cmd[%d]=%02x ", i, SCpnt->cmnd[i])); + VDEB(printk("\n")); + + data->current_SC = SCpnt; + data->current_SC->scsi_done = done; + data->current_SC->SCp.phase = command_ph; + data->current_SC->SCp.Status = 0; + data->current_SC->SCp.Message = 0; + + /* We are locked here already by the mid layer */ + REG0(port_base); + outb(SCpnt->device->id, port_base + DEST_ID); /* set destination */ + outb(FLUSH_FIFO, port_base + CMD_REG); /* reset the fifos */ + + for (i = 0; i < SCpnt->cmd_len; i++) { + outb(SCpnt->cmnd[i], port_base + SCSI_FIFO); + } + outb(SELECT_NO_ATN, port_base + CMD_REG); + + return 0; +} + +static int +SYM53C500_host_reset(struct scsi_cmnd *SCpnt) +{ + int port_base = SCpnt->device->host->io_port; + + DEB(printk("SYM53C500_host_reset called\n")); + SYM53C500_int_host_reset(port_base); + + return SUCCESS; +} + +static int +SYM53C500_biosparm(struct scsi_device *disk, + struct block_device *dev, + sector_t capacity, int *info_array) +{ + int size; + + DEB(printk("SYM53C500_biosparm called\n")); + + size = capacity; + info_array[0] = 64; /* heads */ + info_array[1] = 32; /* sectors */ + info_array[2] = size >> 11; /* cylinders */ + if (info_array[2] > 1024) { /* big disk */ + info_array[0] = 255; + info_array[1] = 63; + info_array[2] = size / (255 * 63); + } + return 0; +} + +static ssize_t +SYM53C500_show_pio(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *SHp = class_to_shost(cdev); + struct sym53c500_data *data = + (struct sym53c500_data *)SHp->hostdata; + + return snprintf(buf, 4, "%d\n", data->fast_pio); +} + +static ssize_t +SYM53C500_store_pio(struct class_device *cdev, const char *buf, size_t count) +{ + int pio; + struct Scsi_Host *SHp = class_to_shost(cdev); + struct sym53c500_data *data = + (struct sym53c500_data *)SHp->hostdata; + + pio = simple_strtoul(buf, NULL, 0); + if (pio == 0 || pio == 1) { + data->fast_pio = pio; + return count; + } + else + return -EINVAL; +} + +/* +* SCSI HBA device attributes we want to +* make available via sysfs. +*/ +static struct class_device_attribute SYM53C500_pio_attr = { + .attr = { + .name = "fast_pio", + .mode = (S_IRUGO | S_IWUSR), + }, + .show = SYM53C500_show_pio, + .store = SYM53C500_store_pio, +}; + +static struct class_device_attribute *SYM53C500_shost_attrs[] = { + &SYM53C500_pio_attr, + NULL, +}; + +/* +* scsi_host_template initializer +*/ +static struct scsi_host_template sym53c500_driver_template = { + .module = THIS_MODULE, + .name = "SYM53C500", + .info = SYM53C500_info, + .queuecommand = SYM53C500_queue, + .eh_host_reset_handler = SYM53C500_host_reset, + .bios_param = SYM53C500_biosparm, + .proc_name = "SYM53C500", + .can_queue = 1, + .this_id = 7, + .sg_tablesize = 32, + .cmd_per_lun = 1, + .use_clustering = ENABLE_CLUSTERING, + .shost_attrs = SYM53C500_shost_attrs +}; + +#define CS_CHECK(fn, ret) \ +do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0) + +static void +SYM53C500_config(dev_link_t *link) +{ + client_handle_t handle = link->handle; + struct scsi_info_t *info = link->priv; + tuple_t tuple; + cisparse_t parse; + int i, last_ret, last_fn; + int irq_level, port_base; + unsigned short tuple_data[32]; + struct Scsi_Host *host; + struct scsi_host_template *tpnt = &sym53c500_driver_template; + struct sym53c500_data *data; + + DEBUG(0, "SYM53C500_config(0x%p)\n", link); + + tuple.TupleData = (cisdata_t *)tuple_data; + tuple.TupleDataMax = 64; + tuple.TupleOffset = 0; + tuple.DesiredTuple = CISTPL_CONFIG; + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); + CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple)); + CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse)); + link->conf.ConfigBase = parse.config.base; + + tuple.DesiredTuple = CISTPL_MANFID; + if ((pcmcia_get_first_tuple(handle, &tuple) == CS_SUCCESS) && + (pcmcia_get_tuple_data(handle, &tuple) == CS_SUCCESS)) + info->manf_id = le16_to_cpu(tuple.TupleData[0]); + + /* Configure card */ + link->state |= DEV_CONFIG; + + tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); + while (1) { + if (pcmcia_get_tuple_data(handle, &tuple) != 0 || + pcmcia_parse_tuple(handle, &tuple, &parse) != 0) + goto next_entry; + link->conf.ConfigIndex = parse.cftable_entry.index; + link->io.BasePort1 = parse.cftable_entry.io.win[0].base; + link->io.NumPorts1 = parse.cftable_entry.io.win[0].len; + + if (link->io.BasePort1 != 0) { + i = pcmcia_request_io(handle, &link->io); + if (i == CS_SUCCESS) + break; + } +next_entry: + CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(handle, &tuple)); + } + + CS_CHECK(RequestIRQ, pcmcia_request_irq(handle, &link->irq)); + CS_CHECK(RequestConfiguration, pcmcia_request_configuration(handle, &link->conf)); + + /* + * That's the trouble with copying liberally from another driver. + * Some things probably aren't relevant, and I suspect this entire + * section dealing with manufacturer IDs can be scrapped. --rct + */ + if ((info->manf_id == MANFID_MACNICA) || + (info->manf_id == MANFID_PIONEER) || + (info->manf_id == 0x0098)) { + /* set ATAcmd */ + outb(0xb4, link->io.BasePort1 + 0xd); + outb(0x24, link->io.BasePort1 + 0x9); + outb(0x04, link->io.BasePort1 + 0xd); + } + + /* + * irq_level == 0 implies tpnt->can_queue == 0, which + * is not supported in 2.6. Thus, only irq_level > 0 + * will be allowed. + * + * Possible port_base values are as follows: + * + * 0x130, 0x230, 0x280, 0x290, + * 0x320, 0x330, 0x340, 0x350 + */ + port_base = link->io.BasePort1; + irq_level = link->irq.AssignedIRQ; + + DEB(printk("SYM53C500: port_base=0x%x, irq=%d, fast_pio=%d\n", + port_base, irq_level, USE_FAST_PIO);) + + chip_init(port_base); + + host = scsi_host_alloc(tpnt, sizeof(struct sym53c500_data)); + if (!host) { + printk("SYM53C500: Unable to register host, giving up.\n"); + goto err_release; + } + + data = (struct sym53c500_data *)host->hostdata; + + if (irq_level > 0) { + if (request_irq(irq_level, SYM53C500_intr, 0, "SYM53C500", host)) { + printk("SYM53C500: unable to allocate IRQ %d\n", irq_level); + goto err_free_scsi; + } + DEB(printk("SYM53C500: allocated IRQ %d\n", irq_level)); + } else if (irq_level == 0) { + DEB(printk("SYM53C500: No interrupts detected\n")); + goto err_free_scsi; + } else { + DEB(printk("SYM53C500: Shouldn't get here!\n")); + goto err_free_scsi; + } + + host->unique_id = port_base; + host->irq = irq_level; + host->io_port = port_base; + host->n_io_port = 0x10; + host->dma_channel = -1; + + /* + * Note fast_pio is set to USE_FAST_PIO by + * default, but can be changed via "sysfs". + */ + data->fast_pio = USE_FAST_PIO; + + sprintf(info->node.dev_name, "scsi%d", host->host_no); + link->dev = &info->node; + info->host = host; + + if (scsi_add_host(host, NULL)) + goto err_free_irq; + + scsi_scan_host(host); + + goto out; /* SUCCESS */ + +err_free_irq: + free_irq(irq_level, host); +err_free_scsi: + scsi_host_put(host); +err_release: + release_region(port_base, 0x10); + printk(KERN_INFO "sym53c500_cs: no SCSI devices found\n"); + +out: + link->state &= ~DEV_CONFIG_PENDING; + return; + +cs_failed: + cs_error(link->handle, last_fn, last_ret); + SYM53C500_release(link); + return; +} /* SYM53C500_config */ + +static int +SYM53C500_event(event_t event, int priority, event_callback_args_t *args) +{ + dev_link_t *link = args->client_data; + struct scsi_info_t *info = link->priv; + + DEBUG(1, "SYM53C500_event(0x%06x)\n", event); + + switch (event) { + case CS_EVENT_CARD_REMOVAL: + link->state &= ~DEV_PRESENT; + if (link->state & DEV_CONFIG) + SYM53C500_release(link); + break; + case CS_EVENT_CARD_INSERTION: + link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; + SYM53C500_config(link); + break; + case CS_EVENT_PM_SUSPEND: + link->state |= DEV_SUSPEND; + /* Fall through... */ + case CS_EVENT_RESET_PHYSICAL: + if (link->state & DEV_CONFIG) + pcmcia_release_configuration(link->handle); + break; + case CS_EVENT_PM_RESUME: + link->state &= ~DEV_SUSPEND; + /* Fall through... */ + case CS_EVENT_CARD_RESET: + if (link->state & DEV_CONFIG) { + pcmcia_request_configuration(link->handle, &link->conf); + /* See earlier comment about manufacturer IDs. */ + if ((info->manf_id == MANFID_MACNICA) || + (info->manf_id == MANFID_PIONEER) || + (info->manf_id == 0x0098)) { + outb(0x80, link->io.BasePort1 + 0xd); + outb(0x24, link->io.BasePort1 + 0x9); + outb(0x04, link->io.BasePort1 + 0xd); + } + /* + * If things don't work after a "resume", + * this is a good place to start looking. + */ + SYM53C500_int_host_reset(link->io.BasePort1); + } + break; + } + return 0; +} /* SYM53C500_event */ + +static void +SYM53C500_detach(dev_link_t *link) +{ + dev_link_t **linkp; + + DEBUG(0, "SYM53C500_detach(0x%p)\n", link); + + /* Locate device structure */ + for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next) + if (*linkp == link) + break; + if (*linkp == NULL) + return; + + if (link->state & DEV_CONFIG) + SYM53C500_release(link); + + if (link->handle) + pcmcia_deregister_client(link->handle); + + /* Unlink device structure, free bits. */ + *linkp = link->next; + kfree(link->priv); + link->priv = NULL; +} /* SYM53C500_detach */ + +static dev_link_t * +SYM53C500_attach(void) +{ + struct scsi_info_t *info; + client_reg_t client_reg; + dev_link_t *link; + int i, ret; + + DEBUG(0, "SYM53C500_attach()\n"); + + /* Create new SCSI device */ + info = kmalloc(sizeof(*info), GFP_KERNEL); + if (!info) + return NULL; + memset(info, 0, sizeof(*info)); + link = &info->link; + link->priv = info; + link->io.NumPorts1 = 16; + link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO; + link->io.IOAddrLines = 10; + link->irq.Attributes = IRQ_TYPE_EXCLUSIVE; + link->irq.IRQInfo1 = IRQ_INFO2_VALID | IRQ_LEVEL_ID; + if (irq_list[0] == -1) + link->irq.IRQInfo2 = irq_mask; + else + for (i = 0; i < 4; i++) + link->irq.IRQInfo2 |= 1 << irq_list[i]; + link->conf.Attributes = CONF_ENABLE_IRQ; + link->conf.Vcc = 50; + link->conf.IntType = INT_MEMORY_AND_IO; + link->conf.Present = PRESENT_OPTION; + + /* Register with Card Services */ + link->next = dev_list; + dev_list = link; + client_reg.dev_info = &dev_info; + client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE; + client_reg.event_handler = &SYM53C500_event; + client_reg.EventMask = CS_EVENT_RESET_REQUEST | CS_EVENT_CARD_RESET | + CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL | + CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME; + client_reg.Version = 0x0210; + client_reg.event_callback_args.client_data = link; + ret = pcmcia_register_client(&link->handle, &client_reg); + if (ret != 0) { + cs_error(link->handle, RegisterClient, ret); + SYM53C500_detach(link); + return NULL; + } + + return link; +} /* SYM53C500_attach */ + +MODULE_AUTHOR("Bob Tracy "); +MODULE_DESCRIPTION("SYM53C500 PCMCIA SCSI driver"); +MODULE_LICENSE("GPL"); + +static struct pcmcia_driver sym53c500_cs_driver = { + .owner = THIS_MODULE, + .drv = { + .name = "sym53c500_cs", + }, + .attach = SYM53C500_attach, + .detach = SYM53C500_detach, +}; + +static int __init +init_sym53c500_cs(void) +{ + return pcmcia_register_driver(&sym53c500_cs_driver); +} + +static void __exit +exit_sym53c500_cs(void) +{ + pcmcia_unregister_driver(&sym53c500_cs_driver); +} + +module_init(init_sym53c500_cs); +module_exit(exit_sym53c500_cs); diff --git a/drivers/scsi/qlogicfas.h b/drivers/scsi/qlogicfas408.h similarity index 73% rename from drivers/scsi/qlogicfas.h rename to drivers/scsi/qlogicfas408.h index 6750e8da6..f01cbd66c 100644 --- a/drivers/scsi/qlogicfas.h +++ b/drivers/scsi/qlogicfas408.h @@ -1,16 +1,10 @@ /* to be used by qlogicfas and qlogic_cs */ -#ifndef __QLOGICFAS_H -#define __QLOGICFAS_H +#ifndef __QLOGICFAS408_H +#define __QLOGICFAS408_H /*----------------------------------------------------------------*/ /* Configuration */ -/* Set the following to 2 to use normal interrupt (active high/totempole- - tristate), otherwise use 0 (REQUIRED FOR PCMCIA) for active low, open - drain */ - -#define QL_INT_ACTIVE_HIGH 2 - /* Set the following to max out the speed of the PIO PseudoDMA transfers, again, 0 tends to be slower, but more stable. */ @@ -79,34 +73,22 @@ the assertion delay, also in 1/2 clocks (FASTCLK is ignored here). */ /*----------------------------------------------------------------*/ -#ifdef PCMCIA -#undef QL_INT_ACTIVE_HIGH -#define QL_INT_ACTIVE_HIGH 0 -#endif - -struct qlogicfas_priv; -typedef struct qlogicfas_priv *qlogicfas_priv_t; -struct qlogicfas_priv { + +struct qlogicfas408_priv { int qbase; /* Port */ int qinitid; /* initiator ID */ int qabort; /* Flag to cause an abort */ int qlirq; /* IRQ being used */ + int int_type; /* type of irq, 2 for ISA board, 0 for PCMCIA */ char qinfo[80]; /* description */ Scsi_Cmnd *qlcmd; /* current command being processed */ - struct Scsi_Host *shost; /* pointer back to host */ - qlogicfas_priv_t next; /* next private struct */ + struct Scsi_Host *shost; /* pointer back to host */ + struct qlogicfas408_priv *next; /* next private struct */ }; -extern int qlcfg5; -extern int qlcfg6; -extern int qlcfg7; -extern int qlcfg8; -extern int qlcfg9; -extern int qlcfgc; - /* The qlogic card uses two register maps - These macros select which one */ #define REG0 ( outb( inb( qbase + 0xd ) & 0x7f , qbase + 0xd ), outb( 4 , qbase + 0xd )) -#define REG1 ( outb( inb( qbase + 0xd ) | 0x80 , qbase + 0xd ), outb( 0xb4 | QL_INT_ACTIVE_HIGH , qbase + 0xd )) +#define REG1 ( outb( inb( qbase + 0xd ) | 0x80 , qbase + 0xd ), outb( 0xb4 | int_type, qbase + 0xd )) /* following is watchdog timeout in microseconds */ #define WATCHDOG 5000000 @@ -115,10 +97,24 @@ extern int qlcfgc; /* the following will set the monitor border color (useful to find where something crashed or gets stuck at and as a simple profiler) */ -#if 0 -#define rtrc(i) {inb(0x3da);outb(0x31,0x3c0);outb((i),0x3c0);} -#else #define rtrc(i) {} -#endif -#endif /* __QLOGICFAS_H */ + +#define get_priv_by_cmd(x) (struct qlogicfas408_priv *)&((x)->device->host->hostdata[0]) +#define get_priv_by_host(x) (struct qlogicfas408_priv *)&((x)->hostdata[0]) + +irqreturn_t qlogicfas408_ihandl(int irq, void *dev_id, struct pt_regs *regs); +int qlogicfas408_queuecommand(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *)); +int qlogicfas408_biosparam(struct scsi_device * disk, + struct block_device *dev, + sector_t capacity, int ip[]); +int qlogicfas408_abort(Scsi_Cmnd * cmd); +int qlogicfas408_bus_reset(Scsi_Cmnd * cmd); +int qlogicfas408_host_reset(Scsi_Cmnd * cmd); +int qlogicfas408_device_reset(Scsi_Cmnd * cmd); +const char *qlogicfas408_info(struct Scsi_Host *host); +int qlogicfas408_get_chip_type(int qbase, int int_type); +void qlogicfas408_setup(int qbase, int id, int int_type); +int qlogicfas408_detect(int qbase, int int_type); +void qlogicfas408_disable_ints(struct qlogicfas408_priv *priv); +#endif /* __QLOGICFAS408_H */ diff --git a/drivers/scsi/sata_promise.h b/drivers/scsi/sata_promise.h new file mode 100644 index 000000000..6e7e96b9e --- /dev/null +++ b/drivers/scsi/sata_promise.h @@ -0,0 +1,154 @@ +/* + * sata_promise.h - Promise SATA common definitions and inline funcs + * + * Copyright 2003-2004 Red Hat, Inc. + * + * The contents of this file are subject to the Open + * Software License version 1.1 that can be found at + * http://www.opensource.org/licenses/osl-1.1.txt and is included herein + * by reference. + * + * Alternatively, the contents of this file may be used under the terms + * of the GNU General Public License version 2 (the "GPL") as distributed + * in the kernel source COPYING file, in which case the provisions of + * the GPL are applicable instead of the above. If you wish to allow + * the use of your version of this file only under the terms of the + * GPL and not to allow others to use your version of this file under + * the OSL, indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by the GPL. + * If you do not delete the provisions above, a recipient may use your + * version of this file under either the OSL or the GPL. + * + */ + +#ifndef __SATA_PROMISE_H__ +#define __SATA_PROMISE_H__ + +#include + +enum pdc_packet_bits { + PDC_PKT_READ = (1 << 2), + PDC_PKT_NODATA = (1 << 3), + + PDC_PKT_SIZEMASK = (1 << 7) | (1 << 6) | (1 << 5), + PDC_PKT_CLEAR_BSY = (1 << 4), + PDC_PKT_WAIT_DRDY = (1 << 3) | (1 << 4), + PDC_LAST_REG = (1 << 3), + + PDC_REG_DEVCTL = (1 << 3) | (1 << 2) | (1 << 1), +}; + +static inline unsigned int pdc_pkt_header(struct ata_taskfile *tf, + dma_addr_t sg_table, + unsigned int devno, u8 *buf) +{ + u8 dev_reg; + u32 *buf32 = (u32 *) buf; + + /* set control bits (byte 0), zero delay seq id (byte 3), + * and seq id (byte 2) + */ + switch (tf->protocol) { + case ATA_PROT_DMA: + if (!(tf->flags & ATA_TFLAG_WRITE)) + buf32[0] = cpu_to_le32(PDC_PKT_READ); + else + buf32[0] = 0; + break; + + case ATA_PROT_NODATA: + buf32[0] = cpu_to_le32(PDC_PKT_NODATA); + break; + + default: + BUG(); + break; + } + + buf32[1] = cpu_to_le32(sg_table); /* S/G table addr */ + buf32[2] = 0; /* no next-packet */ + + if (devno == 0) + dev_reg = ATA_DEVICE_OBS; + else + dev_reg = ATA_DEVICE_OBS | ATA_DEV1; + + /* select device */ + buf[12] = (1 << 5) | PDC_PKT_CLEAR_BSY | ATA_REG_DEVICE; + buf[13] = dev_reg; + + /* device control register */ + buf[14] = (1 << 5) | PDC_REG_DEVCTL; + buf[15] = tf->ctl; + + return 16; /* offset of next byte */ +} + +static inline unsigned int pdc_pkt_footer(struct ata_taskfile *tf, u8 *buf, + unsigned int i) +{ + if (tf->flags & ATA_TFLAG_DEVICE) { + buf[i++] = (1 << 5) | ATA_REG_DEVICE; + buf[i++] = tf->device; + } + + /* and finally the command itself; also includes end-of-pkt marker */ + buf[i++] = (1 << 5) | PDC_LAST_REG | ATA_REG_CMD; + buf[i++] = tf->command; + + return i; +} + +static inline unsigned int pdc_prep_lba28(struct ata_taskfile *tf, u8 *buf, unsigned int i) +{ + /* the "(1 << 5)" should be read "(count << 5)" */ + + /* ATA command block registers */ + buf[i++] = (1 << 5) | ATA_REG_FEATURE; + buf[i++] = tf->feature; + + buf[i++] = (1 << 5) | ATA_REG_NSECT; + buf[i++] = tf->nsect; + + buf[i++] = (1 << 5) | ATA_REG_LBAL; + buf[i++] = tf->lbal; + + buf[i++] = (1 << 5) | ATA_REG_LBAM; + buf[i++] = tf->lbam; + + buf[i++] = (1 << 5) | ATA_REG_LBAH; + buf[i++] = tf->lbah; + + return i; +} + +static inline unsigned int pdc_prep_lba48(struct ata_taskfile *tf, u8 *buf, unsigned int i) +{ + /* the "(2 << 5)" should be read "(count << 5)" */ + + /* ATA command block registers */ + buf[i++] = (2 << 5) | ATA_REG_FEATURE; + buf[i++] = tf->hob_feature; + buf[i++] = tf->feature; + + buf[i++] = (2 << 5) | ATA_REG_NSECT; + buf[i++] = tf->hob_nsect; + buf[i++] = tf->nsect; + + buf[i++] = (2 << 5) | ATA_REG_LBAL; + buf[i++] = tf->hob_lbal; + buf[i++] = tf->lbal; + + buf[i++] = (2 << 5) | ATA_REG_LBAM; + buf[i++] = tf->hob_lbam; + buf[i++] = tf->lbam; + + buf[i++] = (2 << 5) | ATA_REG_LBAH; + buf[i++] = tf->hob_lbah; + buf[i++] = tf->lbah; + + return i; +} + + +#endif /* __SATA_PROMISE_H__ */ diff --git a/drivers/usb/core/driverfs.c b/drivers/usb/core/sysfs.c similarity index 96% rename from drivers/usb/core/driverfs.c rename to drivers/usb/core/sysfs.c index 51ff9bbd6..38c64f656 100644 --- a/drivers/usb/core/driverfs.c +++ b/drivers/usb/core/sysfs.c @@ -1,11 +1,11 @@ /* - * drivers/usb/core/driverfs.c + * drivers/usb/core/sysfs.c * * (C) Copyright 2002 David Brownell * (C) Copyright 2002 Greg Kroah-Hartman * (C) Copyright 2002 IBM Corp. * - * All of the driverfs file attributes for usb devices and interfaces. + * All of the sysfs file attributes for usb devices and interfaces. * */ @@ -163,7 +163,7 @@ usb_descriptor_attr (bDeviceProtocol, "%02x\n") usb_descriptor_attr (bNumConfigurations, "%d\n") -void usb_create_driverfs_dev_files (struct usb_device *udev) +void usb_create_sysfs_dev_files (struct usb_device *udev) { struct device *dev = &udev->dev; @@ -217,7 +217,7 @@ usb_intf_attr (bInterfaceSubClass, "%02x\n") usb_intf_attr (bInterfaceProtocol, "%02x\n") usb_intf_attr (iInterface, "%02x\n") -void usb_create_driverfs_intf_files (struct usb_interface *intf) +void usb_create_sysfs_intf_files (struct usb_interface *intf) { device_create_file (&intf->dev, &dev_attr_bInterfaceNumber); device_create_file (&intf->dev, &dev_attr_bAlternateSetting); diff --git a/drivers/usb/input/touchkitusb.c b/drivers/usb/input/touchkitusb.c new file mode 100644 index 000000000..4917b042e --- /dev/null +++ b/drivers/usb/input/touchkitusb.c @@ -0,0 +1,310 @@ +/****************************************************************************** + * touchkitusb.c -- Driver for eGalax TouchKit USB Touchscreens + * + * Copyright (C) 2004 by Daniel Ritz + * Copyright (C) by Todd E. Johnson (mtouchusb.c) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Based upon mtouchusb.c + * + *****************************************************************************/ + +//#define DEBUG + +#include +#include +#include +#include +#include +#include + +#if !defined(DEBUG) && defined(CONFIG_USB_DEBUG) +#define DEBUG +#endif +#include + + +#define TOUCHKIT_MIN_XC 0x0 +#define TOUCHKIT_MAX_XC 0x07ff +#define TOUCHKIT_XC_FUZZ 0x0 +#define TOUCHKIT_XC_FLAT 0x0 +#define TOUCHKIT_MIN_YC 0x0 +#define TOUCHKIT_MAX_YC 0x07ff +#define TOUCHKIT_YC_FUZZ 0x0 +#define TOUCHKIT_YC_FLAT 0x0 +#define TOUCHKIT_REPORT_DATA_SIZE 8 + +#define TOUCHKIT_DOWN 0x01 +#define TOUCHKIT_POINT_TOUCH 0x81 +#define TOUCHKIT_POINT_NOTOUCH 0x80 + +#define TOUCHKIT_GET_TOUCHED(dat) ((((dat)[0]) & TOUCHKIT_DOWN) ? 1 : 0) +#define TOUCHKIT_GET_X(dat) (((dat)[3] << 7) | (dat)[4]) +#define TOUCHKIT_GET_Y(dat) (((dat)[1] << 7) | (dat)[2]) + +#define DRIVER_VERSION "v0.1" +#define DRIVER_AUTHOR "Daniel Ritz " +#define DRIVER_DESC "eGalax TouchKit USB HID Touchscreen Driver" + +struct touchkit_usb { + unsigned char *data; + dma_addr_t data_dma; + struct urb *irq; + struct usb_device *udev; + struct input_dev input; + int open; + char name[128]; + char phys[64]; +}; + +static struct usb_device_id touchkit_devices[] = { + {USB_DEVICE(0x3823, 0x0001)}, + {USB_DEVICE(0x0eef, 0x0001)}, + {} +}; + +static void touchkit_irq(struct urb *urb, struct pt_regs *regs) +{ + struct touchkit_usb *touchkit = urb->context; + int retval; + + switch (urb->status) { + case 0: + /* success */ + break; + case -ETIMEDOUT: + /* this urb is timing out */ + dbg("%s - urb timed out - was the device unplugged?", + __FUNCTION__); + return; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* this urb is terminated, clean up */ + dbg("%s - urb shutting down with status: %d", + __FUNCTION__, urb->status); + return; + default: + dbg("%s - nonzero urb status received: %d", + __FUNCTION__, urb->status); + goto exit; + } + + input_regs(&touchkit->input, regs); + input_report_key(&touchkit->input, BTN_TOUCH, + TOUCHKIT_GET_TOUCHED(touchkit->data)); + input_report_abs(&touchkit->input, ABS_X, + TOUCHKIT_GET_X(touchkit->data)); + input_report_abs(&touchkit->input, ABS_Y, + TOUCHKIT_GET_Y(touchkit->data)); + input_sync(&touchkit->input); + +exit: + retval = usb_submit_urb(urb, GFP_ATOMIC); + if (retval) + err("%s - usb_submit_urb failed with result: %d", + __FUNCTION__, retval); +} + +static int touchkit_open(struct input_dev *input) +{ + struct touchkit_usb *touchkit = input->private; + + if (touchkit->open++) + return 0; + + touchkit->irq->dev = touchkit->udev; + + if (usb_submit_urb(touchkit->irq, GFP_ATOMIC)) { + touchkit->open--; + return -EIO; + } + + return 0; +} + +static void touchkit_close(struct input_dev *input) +{ + struct touchkit_usb *touchkit = input->private; + + if (!--touchkit->open) + usb_unlink_urb(touchkit->irq); +} + +static int touchkit_alloc_buffers(struct usb_device *udev, + struct touchkit_usb *touchkit) +{ + touchkit->data = usb_buffer_alloc(udev, TOUCHKIT_REPORT_DATA_SIZE, + SLAB_ATOMIC, &touchkit->data_dma); + + if (!touchkit->data) + return -1; + + return 0; +} + +static void touchkit_free_buffers(struct usb_device *udev, + struct touchkit_usb *touchkit) +{ + if (touchkit->data) + usb_buffer_free(udev, TOUCHKIT_REPORT_DATA_SIZE, + touchkit->data, touchkit->data_dma); +} + +static int touchkit_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + int ret; + struct touchkit_usb *touchkit; + struct usb_host_interface *interface; + struct usb_endpoint_descriptor *endpoint; + struct usb_device *udev = interface_to_usbdev(intf); + char path[64]; + char *buf; + + interface = intf->cur_altsetting; + endpoint = &interface->endpoint[0].desc; + + touchkit = kmalloc(sizeof(struct touchkit_usb), GFP_KERNEL); + if (!touchkit) + return -ENOMEM; + + memset(touchkit, 0, sizeof(struct touchkit_usb)); + touchkit->udev = udev; + + if (touchkit_alloc_buffers(udev, touchkit)) { + ret = -ENOMEM; + goto out_free; + } + + touchkit->input.private = touchkit; + touchkit->input.open = touchkit_open; + touchkit->input.close = touchkit_close; + + usb_make_path(udev, path, 64); + sprintf(touchkit->phys, "%s/input0", path); + + touchkit->input.name = touchkit->name; + touchkit->input.phys = touchkit->phys; + touchkit->input.id.bustype = BUS_USB; + touchkit->input.id.vendor = udev->descriptor.idVendor; + touchkit->input.id.product = udev->descriptor.idProduct; + touchkit->input.id.version = udev->descriptor.bcdDevice; + touchkit->input.dev = &intf->dev; + + touchkit->input.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); + touchkit->input.absbit[0] = BIT(ABS_X) | BIT(ABS_Y); + touchkit->input.keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH); + + /* Used to Scale Compensated Data */ + touchkit->input.absmin[ABS_X] = TOUCHKIT_MIN_XC; + touchkit->input.absmax[ABS_X] = TOUCHKIT_MAX_XC; + touchkit->input.absfuzz[ABS_X] = TOUCHKIT_XC_FUZZ; + touchkit->input.absflat[ABS_X] = TOUCHKIT_XC_FLAT; + touchkit->input.absmin[ABS_Y] = TOUCHKIT_MIN_YC; + touchkit->input.absmax[ABS_Y] = TOUCHKIT_MAX_YC; + touchkit->input.absfuzz[ABS_Y] = TOUCHKIT_YC_FUZZ; + touchkit->input.absflat[ABS_Y] = TOUCHKIT_YC_FLAT; + + buf = kmalloc(63, GFP_KERNEL); + if (!buf) { + ret = -ENOMEM; + goto out_free_buffers; + } + + if (udev->descriptor.iManufacturer && + usb_string(udev, udev->descriptor.iManufacturer, buf, 63) > 0) + strcat(touchkit->name, buf); + if (udev->descriptor.iProduct && + usb_string(udev, udev->descriptor.iProduct, buf, 63) > 0) + sprintf(touchkit->name, "%s %s", touchkit->name, buf); + + if (!strlen(touchkit->name)) + sprintf(touchkit->name, "USB Touchscreen %04x:%04x", + touchkit->input.id.vendor, touchkit->input.id.product); + + kfree(buf); + + touchkit->irq = usb_alloc_urb(0, GFP_KERNEL); + if (!touchkit->irq) { + dbg("%s - usb_alloc_urb failed: touchkit->irq", __FUNCTION__); + ret = -ENOMEM; + goto out_free_buffers; + } + + usb_fill_int_urb(touchkit->irq, touchkit->udev, + usb_rcvintpipe(touchkit->udev, 0x81), + touchkit->data, TOUCHKIT_REPORT_DATA_SIZE, + touchkit_irq, touchkit, endpoint->bInterval); + + input_register_device(&touchkit->input); + + printk(KERN_INFO "input: %s on %s\n", touchkit->name, path); + usb_set_intfdata(intf, touchkit); + + return 0; + +out_free_buffers: + touchkit_free_buffers(udev, touchkit); +out_free: + kfree(touchkit); + return ret; +} + +static void touchkit_disconnect(struct usb_interface *intf) +{ + struct touchkit_usb *touchkit = usb_get_intfdata(intf); + + dbg("%s - called", __FUNCTION__); + + if (!touchkit) + return; + + dbg("%s - touchkit is initialized, cleaning up", __FUNCTION__); + usb_set_intfdata(intf, NULL); + input_unregister_device(&touchkit->input); + usb_unlink_urb(touchkit->irq); + usb_free_urb(touchkit->irq); + touchkit_free_buffers(interface_to_usbdev(intf), touchkit); + kfree(touchkit); +} + +MODULE_DEVICE_TABLE(usb, touchkit_devices); + +static struct usb_driver touchkit_driver = { + .owner = THIS_MODULE, + .name = "touchkitusb", + .probe = touchkit_probe, + .disconnect = touchkit_disconnect, + .id_table = touchkit_devices, +}; + +static int __init touchkit_init(void) +{ + return usb_register(&touchkit_driver); +} + +static void __exit touchkit_cleanup(void) +{ + usb_deregister(&touchkit_driver); +} + +module_init(touchkit_init); +module_exit(touchkit_cleanup); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/misc/phidgetservo.c b/drivers/usb/misc/phidgetservo.c new file mode 100644 index 000000000..9018774ae --- /dev/null +++ b/drivers/usb/misc/phidgetservo.c @@ -0,0 +1,327 @@ +/* + * USB PhidgetServo driver 1.0 + * + * Copyright (C) 2004 Sean Young + * + * 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 is a driver for the USB PhidgetServo version 2.0 and 3.0 servo + * controllers available at: http://www.phidgets.com/ + * + * Note that the driver takes input as: degrees.minutes + * -23 < degrees < 203 + * 0 < minutes < 59 + * + * CAUTION: Generally you should use 0 < degrees < 180 as anything else + * is probably beyond the range of your servo and may damage it. + */ + +#include +#ifdef CONFIG_USB_DEBUG +#define DEBUG 1 +#endif +#include +#include +#include +#include +#include +#include + +#define DRIVER_AUTHOR "Sean Young " +#define DRIVER_DESC "USB PhidgetServo Driver" + +#define VENDOR_ID_GLAB 0x06c2 +#define DEVICE_ID_4MOTOR_SERVO_30 0x0038 +#define DEVICE_ID_1MOTOR_SERVO_30 0x0039 + +#define VENDOR_ID_WISEGROUP 0x0925 +#define DEVICE_ID_1MOTOR_SERVO_20 0x8101 +#define DEVICE_ID_4MOTOR_SERVO_20 0x8104 + +static struct usb_device_id id_table[] = { + {USB_DEVICE(VENDOR_ID_GLAB, DEVICE_ID_4MOTOR_SERVO_30)}, + {USB_DEVICE(VENDOR_ID_GLAB, DEVICE_ID_1MOTOR_SERVO_30)}, + {USB_DEVICE(VENDOR_ID_WISEGROUP, DEVICE_ID_4MOTOR_SERVO_20)}, + {USB_DEVICE(VENDOR_ID_WISEGROUP, DEVICE_ID_1MOTOR_SERVO_20)}, + {} +}; + +MODULE_DEVICE_TABLE(usb, id_table); + +struct phidget_servo { + struct usb_device *udev; + int version; + int quad_servo; + int pulse[4]; + int degrees[4]; + int minutes[4]; +}; + +static void +change_position_v30(struct phidget_servo *servo, int servo_no, int degrees, + int minutes) +{ + int retval; + unsigned char *buffer; + + buffer = kmalloc(6, GFP_KERNEL); + if (!buffer) { + dev_err(&servo->udev->dev, "%s - out of memory\n", + __FUNCTION__); + return; + } + + /* + * pulse = 0 - 4095 + * angle = 0 - 180 degrees + * + * pulse = angle * 10.6 + 243.8 + */ + servo->pulse[servo_no] = ((degrees*60 + minutes)*106 + 2438*60)/600; + servo->degrees[servo_no]= degrees; + servo->minutes[servo_no]= minutes; + + /* + * The PhidgetServo v3.0 is controlled by sending 6 bytes, + * 4 * 12 bits for each servo. + * + * low = lower 8 bits pulse + * high = higher 4 bits pulse + * + * offset bits + * +---+-----------------+ + * | 0 | low 0 | + * +---+--------+--------+ + * | 1 | high 1 | high 0 | + * +---+--------+--------+ + * | 2 | low 1 | + * +---+-----------------+ + * | 3 | low 2 | + * +---+--------+--------+ + * | 4 | high 3 | high 2 | + * +---+--------+--------+ + * | 5 | low 3 | + * +---+-----------------+ + */ + + buffer[0] = servo->pulse[0] & 0xff; + buffer[1] = (servo->pulse[0] >> 8 & 0x0f) + | (servo->pulse[1] >> 4 & 0xf0); + buffer[2] = servo->pulse[1] & 0xff; + buffer[3] = servo->pulse[2] & 0xff; + buffer[4] = (servo->pulse[2] >> 8 & 0x0f) + | (servo->pulse[3] >> 4 & 0xf0); + buffer[5] = servo->pulse[3] & 0xff; + + dev_dbg(&servo->udev->dev, + "data: %02x %02x %02x %02x %02x %02x\n", + buffer[0], buffer[1], buffer[2], + buffer[3], buffer[4], buffer[5]); + + retval = usb_control_msg(servo->udev, + usb_sndctrlpipe(servo->udev, 0), + 0x09, 0x21, 0x0200, 0x0000, buffer, 6, 2 * HZ); + if (retval != 6) + dev_err(&servo->udev->dev, "retval = %d\n", retval); + kfree(buffer); +} + +static void +change_position_v20(struct phidget_servo *servo, int servo_no, int degrees, + int minutes) +{ + int retval; + unsigned char *buffer; + + buffer = kmalloc(2, GFP_KERNEL); + if (!buffer) { + dev_err(&servo->udev->dev, "%s - out of memory\n", + __FUNCTION__); + return; + } + + /* + * angle = 0 - 180 degrees + * pulse = angle + 23 + */ + servo->pulse[servo_no]= degrees + 23; + servo->degrees[servo_no]= degrees; + servo->minutes[servo_no]= 0; + + /* + * The PhidgetServo v2.0 is controlled by sending two bytes. The + * first byte is the servo number xor'ed with 2: + * + * servo 0 = 2 + * servo 1 = 3 + * servo 2 = 0 + * servo 3 = 1 + * + * The second byte is the position. + */ + + buffer[0] = servo_no ^ 2; + buffer[1] = servo->pulse[servo_no]; + + dev_dbg(&servo->udev->dev, "data: %02x %02x\n", buffer[0], buffer[1]); + + retval = usb_control_msg(servo->udev, + usb_sndctrlpipe(servo->udev, 0), + 0x09, 0x21, 0x0200, 0x0000, buffer, 2, 2 * HZ); + if (retval != 2) + dev_err(&servo->udev->dev, "retval = %d\n", retval); + kfree(buffer); +} + +#define show_set(value) \ +static ssize_t set_servo##value (struct device *dev, \ + const char *buf, size_t count) \ +{ \ + int degrees, minutes; \ + struct usb_interface *intf = to_usb_interface (dev); \ + struct phidget_servo *servo = usb_get_intfdata (intf); \ + \ + minutes = 0; \ + /* must at least convert degrees */ \ + if (sscanf (buf, "%d.%d", °rees, &minutes) < 1) { \ + return -EINVAL; \ + } \ + \ + if (degrees < -23 || degrees > (180 + 23) || \ + minutes < 0 || minutes > 59) { \ + return -EINVAL; \ + } \ + \ + if (servo->version >= 3) \ + change_position_v30 (servo, value, degrees, minutes); \ + else \ + change_position_v20 (servo, value, degrees, minutes); \ + \ + return count; \ +} \ + \ +static ssize_t show_servo##value (struct device *dev, char *buf) \ +{ \ + struct usb_interface *intf = to_usb_interface (dev); \ + struct phidget_servo *servo = usb_get_intfdata (intf); \ + \ + return sprintf (buf, "%d.%02d\n", servo->degrees[value], \ + servo->minutes[value]); \ +} \ +static DEVICE_ATTR(servo##value, S_IWUGO | S_IRUGO, \ + show_servo##value, set_servo##value); + +show_set(0); +show_set(1); +show_set(2); +show_set(3); + +static int +servo_probe(struct usb_interface *interface, const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(interface); + struct phidget_servo *dev = NULL; + + dev = kmalloc(sizeof (struct phidget_servo), GFP_KERNEL); + if (dev == NULL) { + dev_err(&interface->dev, "%s - out of memory\n", __FUNCTION__); + return -ENOMEM; + } + memset(dev, 0x00, sizeof (*dev)); + + dev->udev = usb_get_dev(udev); + switch (udev->descriptor.idVendor) { + case VENDOR_ID_WISEGROUP: + dev->version = 2; + break; + case VENDOR_ID_GLAB: + dev->version = 3; + break; + } + switch (udev->descriptor.idProduct) { + case DEVICE_ID_4MOTOR_SERVO_20: + case DEVICE_ID_4MOTOR_SERVO_30: + dev->quad_servo = 1; + break; + case DEVICE_ID_1MOTOR_SERVO_20: + case DEVICE_ID_1MOTOR_SERVO_30: + dev->quad_servo = 0; + break; + } + + usb_set_intfdata(interface, dev); + + device_create_file(&interface->dev, &dev_attr_servo0); + if (dev->quad_servo) { + device_create_file(&interface->dev, &dev_attr_servo1); + device_create_file(&interface->dev, &dev_attr_servo2); + device_create_file(&interface->dev, &dev_attr_servo3); + } + + dev_info(&interface->dev, "USB %d-Motor PhidgetServo v%d.0 attached\n", + dev->quad_servo ? 4 : 1, dev->version); + if (dev->version == 2) + dev_info(&interface->dev, + "WARNING: v2.0 not tested! Please report if it works.\n"); + + return 0; +} + +static void +servo_disconnect(struct usb_interface *interface) +{ + struct phidget_servo *dev; + + dev = usb_get_intfdata(interface); + usb_set_intfdata(interface, NULL); + + device_remove_file(&interface->dev, &dev_attr_servo0); + if (dev->quad_servo) { + device_remove_file(&interface->dev, &dev_attr_servo1); + device_remove_file(&interface->dev, &dev_attr_servo2); + device_remove_file(&interface->dev, &dev_attr_servo3); + } + + usb_put_dev(dev->udev); + + kfree(dev); + + dev_info(&interface->dev, "USB %d-Motor PhidgetServo v%d.0 detached\n", + dev->quad_servo ? 4 : 1, dev->version); +} + +static struct usb_driver servo_driver = { + .owner = THIS_MODULE, + .name = "phidgetservo", + .probe = servo_probe, + .disconnect = servo_disconnect, + .id_table = id_table +}; + +static int __init +phidget_servo_init(void) +{ + int retval = 0; + + retval = usb_register(&servo_driver); + if (retval) + err("usb_register failed. Error number %d", retval); + + return retval; +} + +static void __exit +phidget_servo_exit(void) +{ + usb_deregister(&servo_driver); +} + +module_init(phidget_servo_init); +module_exit(phidget_servo_exit); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/asiliantfb.c b/drivers/video/asiliantfb.c new file mode 100644 index 000000000..034ec2996 --- /dev/null +++ b/drivers/video/asiliantfb.c @@ -0,0 +1,620 @@ +/* + * drivers/video/asiliantfb.c + * frame buffer driver for Asiliant 69000 chip + * Copyright (C) 2001-2003 Saito.K & Jeanne + * + * from driver/video/chipsfb.c and, + * + * drivers/video/asiliantfb.c -- frame buffer device for + * Asiliant 69030 chip (formerly Intel, formerly Chips & Technologies) + * Author: apc@agelectronics.co.uk + * Copyright (C) 2000 AG Electronics + * Note: the data sheets don't seem to be available from Asiliant. + * They are available by searching developer.intel.com, but are not otherwise + * linked to. + * + * This driver should be portable with minimal effort to the 69000 display + * chip, and to the twin-display mode of the 69030. + * Contains code from Thomas Hhenleitner (thanks) + * + * Derived from the CT65550 driver chipsfb.c: + * Copyright (C) 1998 Paul Mackerras + * ...which was derived from the Powermac "chips" driver: + * Copyright (C) 1997 Fabio Riccardi. + * And from the frame buffer device for Open Firmware-initialized devices: + * Copyright (C) 1997 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 +#include +#include +#include +#include +#include +#include +#include +#include + +/* Built in clock of the 69030 */ +const unsigned Fref = 14318180; + +#define mmio_base (p->screen_base + 0x400000) + +#define mm_write_ind(num, val, ap, dp) do { \ + writeb((num), mmio_base + (ap)); writeb((val), mmio_base + (dp)); \ +} while (0) + +static void mm_write_xr(struct fb_info *p, u8 reg, u8 data) +{ + mm_write_ind(reg, data, 0x7ac, 0x7ad); +} +#define write_xr(num, val) mm_write_xr(p, num, val) + +static void mm_write_fr(struct fb_info *p, u8 reg, u8 data) +{ + mm_write_ind(reg, data, 0x7a0, 0x7a1); +} +#define write_fr(num, val) mm_write_fr(p, num, val) + +static void mm_write_cr(struct fb_info *p, u8 reg, u8 data) +{ + mm_write_ind(reg, data, 0x7a8, 0x7a9); +} +#define write_cr(num, val) mm_write_cr(p, num, val) + +static void mm_write_gr(struct fb_info *p, u8 reg, u8 data) +{ + mm_write_ind(reg, data, 0x79c, 0x79d); +} +#define write_gr(num, val) mm_write_gr(p, num, val) + +static void mm_write_sr(struct fb_info *p, u8 reg, u8 data) +{ + mm_write_ind(reg, data, 0x788, 0x789); +} +#define write_sr(num, val) mm_write_sr(p, num, val) + +static void mm_write_ar(struct fb_info *p, u8 reg, u8 data) +{ + readb(mmio_base + 0x7b4); + mm_write_ind(reg, data, 0x780, 0x780); +} +#define write_ar(num, val) mm_write_ar(p, num, val) + +/* + * Exported functions + */ +int asiliantfb_init(void); + +static int asiliantfb_pci_init(struct pci_dev *dp, const struct pci_device_id *); +static int asiliantfb_check_var(struct fb_var_screeninfo *var, + struct fb_info *info); +static int asiliantfb_set_par(struct fb_info *info); +static int asiliantfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, + u_int transp, struct fb_info *info); + +static struct fb_ops asiliantfb_ops = { + .owner = THIS_MODULE, + .fb_check_var = asiliantfb_check_var, + .fb_set_par = asiliantfb_set_par, + .fb_setcolreg = asiliantfb_setcolreg, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, + .fb_cursor = soft_cursor, +}; + +/* Calculate the ratios for the dot clocks without using a single long long + * value */ +static void asiliant_calc_dclk2(u32 *ppixclock, u8 *dclk2_m, u8 *dclk2_n, u8 *dclk2_div) +{ + unsigned pixclock = *ppixclock; + unsigned Ftarget = 1000000 * (1000000 / pixclock); + unsigned n; + unsigned best_error = 0xffffffff; + unsigned best_m = 0xffffffff, + best_n = 0xffffffff; + unsigned ratio; + unsigned remainder; + unsigned char divisor = 0; + + /* Calculate the frequency required. This is hard enough. */ + ratio = 1000000 / pixclock; + remainder = 1000000 % pixclock; + Ftarget = 1000000 * ratio + (1000000 * remainder) / pixclock; + + while (Ftarget < 100000000) { + divisor += 0x10; + Ftarget <<= 1; + } + + ratio = Ftarget / Fref; + remainder = Ftarget % Fref; + + /* This expresses the constraint that 150kHz <= Fref/n <= 5Mhz, + * together with 3 <= n <= 257. */ + for (n = 3; n <= 257; n++) { + unsigned m = n * ratio + (n * remainder) / Fref; + + /* 3 <= m <= 257 */ + if (m >= 3 && m <= 257) { + unsigned new_error = ((Ftarget * n) - (Fref * m)) >= 0 ? + ((Ftarget * n) - (Fref * m)) : ((Fref * m) - (Ftarget * n)); + if (new_error < best_error) { + best_n = n; + best_m = m; + best_error = new_error; + } + } + /* But if VLD = 4, then 4m <= 1028 */ + else if (m <= 1028) { + /* remember there are still only 8-bits of precision in m, so + * avoid over-optimistic error calculations */ + unsigned new_error = ((Ftarget * n) - (Fref * (m & ~3))) >= 0 ? + ((Ftarget * n) - (Fref * (m & ~3))) : ((Fref * (m & ~3)) - (Ftarget * n)); + if (new_error < best_error) { + best_n = n; + best_m = m; + best_error = new_error; + } + } + } + if (best_m > 257) + best_m >>= 2; /* divide m by 4, and leave VCO loop divide at 4 */ + else + divisor |= 4; /* or set VCO loop divide to 1 */ + *dclk2_m = best_m - 2; + *dclk2_n = best_n - 2; + *dclk2_div = divisor; + *ppixclock = pixclock; + return; +} + +static void asiliant_set_timing(struct fb_info *p) +{ + unsigned hd = p->var.xres / 8; + unsigned hs = (p->var.xres + p->var.right_margin) / 8; + unsigned he = (p->var.xres + p->var.right_margin + p->var.hsync_len) / 8; + unsigned ht = (p->var.left_margin + p->var.xres + p->var.right_margin + p->var.hsync_len) / 8; + unsigned vd = p->var.yres; + unsigned vs = p->var.yres + p->var.lower_margin; + unsigned ve = p->var.yres + p->var.lower_margin + p->var.vsync_len; + unsigned vt = p->var.upper_margin + p->var.yres + p->var.lower_margin + p->var.vsync_len; + unsigned wd = (p->var.xres_virtual * ((p->var.bits_per_pixel+7)/8)) / 8; + + if ((p->var.xres == 640) && (p->var.yres == 480) && (p->var.pixclock == 39722)) { + write_fr(0x01, 0x02); /* LCD */ + } else { + write_fr(0x01, 0x01); /* CRT */ + } + + write_cr(0x11, (ve - 1) & 0x0f); + write_cr(0x00, (ht - 5) & 0xff); + write_cr(0x01, hd - 1); + write_cr(0x02, hd); + write_cr(0x03, ((ht - 1) & 0x1f) | 0x80); + write_cr(0x04, hs); + write_cr(0x05, (((ht - 1) & 0x20) <<2) | (he & 0x1f)); + write_cr(0x3c, (ht - 1) & 0xc0); + write_cr(0x06, (vt - 2) & 0xff); + write_cr(0x30, (vt - 2) >> 8); + write_cr(0x07, 0x00); + write_cr(0x08, 0x00); + write_cr(0x09, 0x00); + write_cr(0x10, (vs - 1) & 0xff); + write_cr(0x32, ((vs - 1) >> 8) & 0xf); + write_cr(0x11, ((ve - 1) & 0x0f) | 0x80); + write_cr(0x12, (vd - 1) & 0xff); + write_cr(0x31, ((vd - 1) & 0xf00) >> 8); + write_cr(0x13, wd & 0xff); + write_cr(0x41, (wd & 0xf00) >> 8); + write_cr(0x15, (vs - 1) & 0xff); + write_cr(0x33, ((vs - 1) >> 8) & 0xf); + write_cr(0x38, ((ht - 5) & 0x100) >> 8); + write_cr(0x16, (vt - 1) & 0xff); + write_cr(0x18, 0x00); + + if (p->var.xres == 640) { + writeb(0xc7, mmio_base + 0x784); /* set misc output reg */ + } else { + writeb(0x07, mmio_base + 0x784); /* set misc output reg */ + } +} + +static int asiliantfb_check_var(struct fb_var_screeninfo *var, + struct fb_info *p) +{ + unsigned long Ftarget, ratio, remainder; + + ratio = 1000000 / var->pixclock; + remainder = 1000000 % var->pixclock; + Ftarget = 1000000 * ratio + (1000000 * remainder) / var->pixclock; + + /* First check the constraint that the maximum post-VCO divisor is 32, + * and the maximum Fvco is 220MHz */ + if (Ftarget > 220000000 || Ftarget < 3125000) { + printk(KERN_ERR "asiliantfb dotclock must be between 3.125 and 220MHz\n"); + return -ENXIO; + } + var->xres_virtual = var->xres; + var->yres_virtual = var->yres; + + if (var->bits_per_pixel == 24) { + var->red.offset = 16; + var->green.offset = 8; + var->blue.offset = 0; + var->red.length = var->blue.length = var->green.length = 8; + } else if (var->bits_per_pixel == 16) { + switch (var->red.offset) { + case 11: + var->green.length = 6; + break; + case 10: + var->green.length = 5; + break; + default: + return -EINVAL; + } + var->green.offset = 5; + var->blue.offset = 0; + var->red.length = var->blue.length = 5; + } else if (var->bits_per_pixel == 8) { + var->red.offset = var->green.offset = var->blue.offset = 0; + var->red.length = var->green.length = var->blue.length = 8; + } + return 0; +} + +static int asiliantfb_set_par(struct fb_info *p) +{ + u8 dclk2_m; /* Holds m-2 value for register */ + u8 dclk2_n; /* Holds n-2 value for register */ + u8 dclk2_div; /* Holds divisor bitmask */ + + /* Set pixclock */ + asiliant_calc_dclk2(&p->var.pixclock, &dclk2_m, &dclk2_n, &dclk2_div); + + /* Set color depth */ + if (p->var.bits_per_pixel == 24) { + write_xr(0x81, 0x16); /* 24 bit packed color mode */ + write_xr(0x82, 0x00); /* Disable palettes */ + write_xr(0x20, 0x20); /* 24 bit blitter mode */ + } else if (p->var.bits_per_pixel == 16) { + if (p->var.red.offset == 11) + write_xr(0x81, 0x15); /* 16 bit color mode */ + else + write_xr(0x81, 0x14); /* 15 bit color mode */ + write_xr(0x82, 0x00); /* Disable palettes */ + write_xr(0x20, 0x10); /* 16 bit blitter mode */ + } else if (p->var.bits_per_pixel == 8) { + write_xr(0x0a, 0x02); /* Linear */ + write_xr(0x81, 0x12); /* 8 bit color mode */ + write_xr(0x82, 0x00); /* Graphics gamma enable */ + write_xr(0x20, 0x00); /* 8 bit blitter mode */ + } + p->fix.line_length = p->var.xres * (p->var.bits_per_pixel >> 3); + p->fix.visual = (p->var.bits_per_pixel == 8) ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR; + write_xr(0xc4, dclk2_m); + write_xr(0xc5, dclk2_n); + write_xr(0xc7, dclk2_div); + /* Set up the CR registers */ + asiliant_set_timing(p); + return 0; +} + +static int asiliantfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, + u_int transp, struct fb_info *p) +{ + if (regno > 255) + return 1; + red >>= 8; + green >>= 8; + blue >>= 8; + + /* Set hardware palete */ + writeb(regno, mmio_base + 0x790); + udelay(1); + writeb(red, mmio_base + 0x791); + writeb(green, mmio_base + 0x791); + writeb(blue, mmio_base + 0x791); + + switch(p->var.bits_per_pixel) { + case 15: + if (regno < 16) { + ((u32 *)(p->pseudo_palette))[regno] = + ((red & 0xf8) << 7) | + ((green & 0xf8) << 2) | + ((blue & 0xf8) >> 3); + } + break; + case 16: + if (regno < 16) { + ((u32 *)(p->pseudo_palette))[regno] = + ((red & 0xf8) << 8) | + ((green & 0xfc) << 3) | + ((blue & 0xf8) >> 3); + } + break; + case 24: + if (regno < 24) { + ((u32 *)(p->pseudo_palette))[regno] = + (red << 16) | + (green << 8) | + (blue); + } + break; + } + return 0; +} + +struct chips_init_reg { + unsigned char addr; + unsigned char data; +}; + +#define N_ELTS(x) (sizeof(x) / sizeof(x[0])) + +static struct chips_init_reg chips_init_sr[] = +{ + {0x00, 0x03}, /* Reset register */ + {0x01, 0x01}, /* Clocking mode */ + {0x02, 0x0f}, /* Plane mask */ + {0x04, 0x0e} /* Memory mode */ +}; + +static struct chips_init_reg chips_init_gr[] = +{ + {0x03, 0x00}, /* Data rotate */ + {0x05, 0x00}, /* Graphics mode */ + {0x06, 0x01}, /* Miscellaneous */ + {0x08, 0x00} /* Bit mask */ +}; + +static struct chips_init_reg chips_init_ar[] = +{ + {0x10, 0x01}, /* Mode control */ + {0x11, 0x00}, /* Overscan */ + {0x12, 0x0f}, /* Memory plane enable */ + {0x13, 0x00} /* Horizontal pixel panning */ +}; + +static struct chips_init_reg chips_init_cr[] = +{ + {0x0c, 0x00}, /* Start address high */ + {0x0d, 0x00}, /* Start address low */ + {0x40, 0x00}, /* Extended Start Address */ + {0x41, 0x00}, /* Extended Start Address */ + {0x14, 0x00}, /* Underline location */ + {0x17, 0xe3}, /* CRT mode control */ + {0x70, 0x00} /* Interlace control */ +}; + + +static struct chips_init_reg chips_init_fr[] = +{ + {0x01, 0x02}, + {0x03, 0x08}, + {0x08, 0xcc}, + {0x0a, 0x08}, + {0x18, 0x00}, + {0x1e, 0x80}, + {0x40, 0x83}, + {0x41, 0x00}, + {0x48, 0x13}, + {0x4d, 0x60}, + {0x4e, 0x0f}, + + {0x0b, 0x01}, + + {0x21, 0x51}, + {0x22, 0x1d}, + {0x23, 0x5f}, + {0x20, 0x4f}, + {0x34, 0x00}, + {0x24, 0x51}, + {0x25, 0x00}, + {0x27, 0x0b}, + {0x26, 0x00}, + {0x37, 0x80}, + {0x33, 0x0b}, + {0x35, 0x11}, + {0x36, 0x02}, + {0x31, 0xea}, + {0x32, 0x0c}, + {0x30, 0xdf}, + {0x10, 0x0c}, + {0x11, 0xe0}, + {0x12, 0x50}, + {0x13, 0x00}, + {0x16, 0x03}, + {0x17, 0xbd}, + {0x1a, 0x00}, +}; + + +static struct chips_init_reg chips_init_xr[] = +{ + {0xce, 0x00}, /* set default memory clock */ + {0xcc, 200 }, /* MCLK ratio M */ + {0xcd, 18 }, /* MCLK ratio N */ + {0xce, 0x90}, /* MCLK divisor = 2 */ + + {0xc4, 209 }, + {0xc5, 118 }, + {0xc7, 32 }, + {0xcf, 0x06}, + {0x09, 0x01}, /* IO Control - CRT controller extensions */ + {0x0a, 0x02}, /* Frame buffer mapping */ + {0x0b, 0x01}, /* PCI burst write */ + {0x40, 0x03}, /* Memory access control */ + {0x80, 0x82}, /* Pixel pipeline configuration 0 */ + {0x81, 0x12}, /* Pixel pipeline configuration 1 */ + {0x82, 0x08}, /* Pixel pipeline configuration 2 */ + + {0xd0, 0x0f}, + {0xd1, 0x01}, +}; + +static void __init chips_hw_init(struct fb_info *p) +{ + int i; + + for (i = 0; i < N_ELTS(chips_init_xr); ++i) + write_xr(chips_init_xr[i].addr, chips_init_xr[i].data); + write_xr(0x81, 0x12); + write_xr(0x82, 0x08); + write_xr(0x20, 0x00); + for (i = 0; i < N_ELTS(chips_init_sr); ++i) + write_sr(chips_init_sr[i].addr, chips_init_sr[i].data); + for (i = 0; i < N_ELTS(chips_init_gr); ++i) + write_gr(chips_init_gr[i].addr, chips_init_gr[i].data); + for (i = 0; i < N_ELTS(chips_init_ar); ++i) + write_ar(chips_init_ar[i].addr, chips_init_ar[i].data); + /* Enable video output in attribute index register */ + writeb(0x20, mmio_base + 0x780); + for (i = 0; i < N_ELTS(chips_init_cr); ++i) + write_cr(chips_init_cr[i].addr, chips_init_cr[i].data); + for (i = 0; i < N_ELTS(chips_init_fr); ++i) + write_fr(chips_init_fr[i].addr, chips_init_fr[i].data); +} + +static struct fb_fix_screeninfo asiliantfb_fix __initdata = { + .id = "Asiliant 69000", + .type = FB_TYPE_PACKED_PIXELS, + .visual = FB_VISUAL_PSEUDOCOLOR, + .accel = FB_ACCEL_NONE, + .line_length = 640, + .smem_len = 0x200000, /* 2MB */ +}; + +static struct fb_var_screeninfo asiliantfb_var __initdata = { + .xres = 640, + .yres = 480, + .xres_virtual = 640, + .yres_virtual = 480, + .bits_per_pixel = 8, + .red = { .length = 8 }, + .green = { .length = 8 }, + .blue = { .length = 8 }, + .height = -1, + .width = -1, + .vmode = FB_VMODE_NONINTERLACED, + .pixclock = 39722, + .left_margin = 48, + .right_margin = 16, + .upper_margin = 33, + .lower_margin = 10, + .hsync_len = 96, + .vsync_len = 2, +}; + +static void __init init_asiliant(struct fb_info *p, unsigned long addr) +{ + p->fix = asiliantfb_fix; + p->fix.smem_start = addr; + p->var = asiliantfb_var; + p->fbops = &asiliantfb_ops; + p->flags = FBINFO_FLAG_DEFAULT; + + fb_alloc_cmap(&p->cmap, 256, 0); + + if (register_framebuffer(p) < 0) { + printk(KERN_ERR "C&T 69000 framebuffer failed to register\n"); + return; + } + + printk(KERN_INFO "fb%d: Asiliant 69000 frame buffer (%dK RAM detected)\n", + p->node, p->fix.smem_len / 1024); + + writeb(0xff, mmio_base + 0x78c); + chips_hw_init(p); +} + +static int __devinit +asiliantfb_pci_init(struct pci_dev *dp, const struct pci_device_id *ent) +{ + unsigned long addr, size; + struct fb_info *p; + + if ((dp->resource[0].flags & IORESOURCE_MEM) == 0) + return -ENODEV; + addr = pci_resource_start(dp, 0); + size = pci_resource_len(dp, 0); + if (addr == 0) + return -ENODEV; + if (!request_mem_region(addr, size, "asiliantfb")) + return -EBUSY; + + p = framebuffer_alloc(sizeof(u32) * 256, &dp->dev); + if (!p) { + release_mem_region(addr, size); + return -ENOMEM; + } + p->pseudo_palette = p->par; + p->par = NULL; + + p->screen_base = ioremap(addr, 0x800000); + if (p->screen_base == NULL) { + release_mem_region(addr, size); + framebuffer_release(p); + return -ENOMEM; + } + + pci_write_config_dword(dp, 4, 0x02800083); + writeb(3, addr + 0x400784); + + init_asiliant(p, addr); + + /* Clear the entire framebuffer */ + memset(p->screen_base, 0, 0x200000); + + pci_set_drvdata(dp, p); + return 0; +} + +static void __devexit asiliantfb_remove(struct pci_dev *dp) +{ + struct fb_info *p = pci_get_drvdata(dp); + + unregister_framebuffer(p); + iounmap(p->screen_base); + release_mem_region(pci_resource_start(dp, 0), pci_resource_len(dp, 0)); + pci_set_drvdata(dp, NULL); + framebuffer_release(p); +} + +static struct pci_device_id asiliantfb_pci_tbl[] __devinitdata = { + { PCI_VENDOR_ID_CT, PCI_DEVICE_ID_CT_69000, PCI_ANY_ID, PCI_ANY_ID }, + { 0 } +}; + +MODULE_DEVICE_TABLE(pci, asiliantfb_pci_tbl); + +static struct pci_driver asiliantfb_driver = { + .name = "asiliantfb", + .id_table = asiliantfb_pci_tbl, + .probe = asiliantfb_pci_init, + .remove = __devexit_p(asiliantfb_remove), +}; + +int __init asiliantfb_init(void) +{ + return pci_module_init(&asiliantfb_driver); +} + +static void __exit asiliantfb_exit(void) +{ + pci_unregister_driver(&asiliantfb_driver); +} + +MODULE_LICENSE("GPL"); diff --git a/drivers/video/gbefb.c b/drivers/video/gbefb.c new file mode 100644 index 000000000..2afc4148b --- /dev/null +++ b/drivers/video/gbefb.c @@ -0,0 +1,1200 @@ +/* + * SGI GBE frame buffer driver + * + * Copyright (C) 1999 Silicon Graphics, Inc. - Jeffrey Newquist + * Copyright (C) 2002 Vivien Chappelier + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive for + * more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_X86 +#include +#endif +#ifdef CONFIG_MIPS +#include +#endif +#include +#include +#include + +#include